From e2ba9565d52345299f8a78adac0037f56e754f18 Mon Sep 17 00:00:00 2001 From: nefrace Date: Sun, 22 Jan 2023 23:54:54 +0300 Subject: [PATCH] Dispatcher, Commands, Filters, Markdown escaping --- bot.go | 5 +---- command.go | 24 +++++++++++++++++++++ dispatcher.go | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ filters.go | 17 +++++++++++++++ markdown.go | 14 +++++++++++++ nechotron.go | 2 +- state.go | 10 ++++----- update.go | 23 +++++++++++--------- 8 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 command.go create mode 100644 dispatcher.go create mode 100644 filters.go create mode 100644 markdown.go diff --git a/bot.go b/bot.go index 73619d0..48deae9 100644 --- a/bot.go +++ b/bot.go @@ -30,11 +30,8 @@ func (b *bot) Update(u *echo.Update) { b.lock.Lock() defer b.lock.Unlock() - newState, err := b.state.Call(upd) + err := b.state.Call(upd) if err != nil { upd.LogError("", err, true) } - if newState != nil { - b.state = newState - } } diff --git a/command.go b/command.go new file mode 100644 index 0000000..894dbee --- /dev/null +++ b/command.go @@ -0,0 +1,24 @@ +package nechotron + +import "strings" + +type Command struct { + Body string + IsAdminOnly bool +} + +func NewCommand(body string, isAdminOnly bool) *Command { + return &Command{ + Body: body, + IsAdminOnly: isAdminOnly, + } +} +func (c *Command) String() string { + return "/" + c.Body +} + +func (c *Command) Param(text string) string { + return strings.TrimPrefix(text, "/"+c.Body+" ") +} + +// func HandleCommand(command *Command, handler cmdFunc) (bool, error) diff --git a/dispatcher.go b/dispatcher.go new file mode 100644 index 0000000..f7a34b9 --- /dev/null +++ b/dispatcher.go @@ -0,0 +1,58 @@ +package nechotron + +import ( + "strings" +) + +type dispatchHandler func(u *Update) (bool, error) +type updateHandler func(u *Update) error +type commandHandler func(u *UpdateCommand) error + +type Dispatcher struct { + handlers []dispatchHandler +} + +func NewDispatcher() *Dispatcher { + return &Dispatcher{} +} + +func (d *Dispatcher) Run(u *Update) error { + for _, h := range d.handlers { + executed, err := h(u) + if executed { + return err + } + } + return nil +} + +func (d *Dispatcher) HandleCommand(command *Command, handler commandHandler) *Dispatcher { + newHandler := func(u *Update) (bool, error) { + if !strings.HasPrefix(u.Text(), command.String()) { + return false, nil + } + if command.IsAdminOnly && !u.IsUserAdmin() { + return false, nil + } + upd := &UpdateCommand{ + Update: *u, + Param: command.Param(u.Text()), + } + err := handler(upd) + return true, err + } + d.handlers = append(d.handlers, newHandler) + return d +} + +func (d *Dispatcher) HandleFilter(filter FilterFn, handler updateHandler) *Dispatcher { + newHandler := func(u *Update) (bool, error) { + if !filter(u) { + return false, nil + } + err := handler(u) + return true, err + } + d.handlers = append(d.handlers, newHandler) + return d +} diff --git a/filters.go b/filters.go new file mode 100644 index 0000000..6e81259 --- /dev/null +++ b/filters.go @@ -0,0 +1,17 @@ +package nechotron + +import "strings" + +type FilterFn func(u *Update) bool + +func TextStartsWith(text string) FilterFn { + return func(u *Update) bool { + return strings.HasPrefix(u.Text(), text) + } +} + +func TextHas(text string) FilterFn { + return func(u *Update) bool { + return strings.Contains(u.Text(), text) + } +} diff --git a/markdown.go b/markdown.go new file mode 100644 index 0000000..ebc7da5 --- /dev/null +++ b/markdown.go @@ -0,0 +1,14 @@ +package nechotron + +import ( + "regexp" + "strings" +) + +var chars = []string{"_", "*", "\\[", "\\]", "\\(", "\\)", "~", "`", ">", "#", "+", "-", "=", "|", "{", "}", ".", "!"} +var r = strings.Join(chars, "") +var reg = regexp.MustCompile("[" + r + "]+") + +func EscapeMd2(s string) string { + return reg.ReplaceAllString(s, "\\$0") +} diff --git a/nechotron.go b/nechotron.go index 52c8ae6..831e4b2 100644 --- a/nechotron.go +++ b/nechotron.go @@ -24,7 +24,7 @@ func NewTron(token string, defaultState *State) *Nechotron { } func (n *Nechotron) newBot(chatID int64) echo.Bot { - a := echo.NewCustomAPI(n.Token, n.ApiServer) + a := echo.NewAPI(n.Token) // a := echo.NewAPI(n.Token) me, _ := a.GetMe() // log.Println("New bot active: ", chatID, me.Result) diff --git a/state.go b/state.go index 6d0ecbc..62f9f7e 100644 --- a/state.go +++ b/state.go @@ -9,18 +9,18 @@ import ( type StateData map[string]interface{} type Runnable interface { - Call(*Update) (Runnable, error) + Call(*Update) error } type State struct { Fn stateFn } -func (s *State) Call(u *Update) (Runnable, error) { +func (s *State) Call(u *Update) error { return s.Fn(u) } -type stateFn func(*Update) (Runnable, error) +type stateFn func(*Update) error func (d StateData) Set(name string, data interface{}) { d[name] = data @@ -49,7 +49,7 @@ var EchoState = State{ Fn: EchoFunc, } -func EchoFunc(u *Update) (Runnable, error) { +func EchoFunc(u *Update) error { u.AnswerText(u.Text(), &echotron.MessageOptions{}) - return nil, nil + return nil } diff --git a/update.go b/update.go index b056a29..6af009b 100644 --- a/update.go +++ b/update.go @@ -13,6 +13,10 @@ type U echotron.Update var emptyOpts = echotron.MessageOptions{} +type Event interface { + Update +} + type Update struct { U Bot *bot @@ -20,6 +24,11 @@ type Update struct { Ctx context.Context } +type UpdateCommand struct { + Update + Param string +} + func (u *Update) Upd() *echotron.Update { return (*echotron.Update)(&u.U) } @@ -169,17 +178,11 @@ func (u *Update) Entities() []*echotron.MessageEntity { } func (u *Update) IsUserAdmin() bool { - ids := []int64{ - 60441930, // v.rud - 327487258, // vika shet + member, err := u.Bot.GetChatMember(u.ChatID(), u.From().ID) + if err != nil { + return false } - from := u.From() - for i := range ids { - if from.ID == ids[i] { - return true - } - } - return false + return member.Result.Status == "administrator" || member.Result.Status == "creator" } func (u *Update) LogError(text string, e error, send bool) {