3 Commits

Author SHA1 Message Date
90e2f38c18 v0.3.6; answerCallbackQuery 2026-02-03 14:51:57 +03:00
0921d306fd some fixes 2026-01-29 12:08:28 +03:00
6970c37c6b bump slog to v1.0.2 2026-01-29 11:59:38 +03:00
9 changed files with 91 additions and 97 deletions

12
bot.go
View File

@@ -30,8 +30,8 @@ type Bot struct {
logger *slog.Logger logger *slog.Logger
requestLogger *slog.Logger requestLogger *slog.Logger
plugins []*Plugin plugins []Plugin
middlewares []*Middleware middlewares []Middleware
prefixes []string prefixes []string
runners []Runner runners []Runner
@@ -75,7 +75,7 @@ func LoadPrefixesFromEnv() []string {
func NewBot(settings *BotSettings) *Bot { func NewBot(settings *BotSettings) *Bot {
updateQueue := CreateQueue[*Update](256) updateQueue := CreateQueue[*Update](256)
bot := &Bot{ bot := &Bot{
updateOffset: 0, plugins: make([]*Plugin, 0), debug: settings.Debug, errorTemplate: "%s", updateOffset: 0, plugins: make([]Plugin, 0), debug: settings.Debug, errorTemplate: "%s",
prefixes: settings.Prefixes, updateTypes: make([]string, 0), runners: make([]Runner, 0), prefixes: settings.Prefixes, updateTypes: make([]string, 0), runners: make([]Runner, 0),
updateQueue: updateQueue, updateQueue: updateQueue,
token: settings.Token, token: settings.Token,
@@ -171,20 +171,20 @@ func (b *Bot) Debug(debug bool) *Bot {
b.debug = debug b.debug = debug
return b return b
} }
func (b *Bot) AddPlugins(plugin ...*Plugin) *Bot { func (b *Bot) AddPlugins(plugin ...Plugin) *Bot {
b.plugins = append(b.plugins, plugin...) b.plugins = append(b.plugins, plugin...)
for _, p := range plugin { for _, p := range plugin {
b.logger.Debugln(fmt.Sprintf("plugins with name \"%s\" registered", p.Name)) b.logger.Debugln(fmt.Sprintf("plugins with name \"%s\" registered", p.Name))
} }
return b return b
} }
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 {

2
go.mod
View File

@@ -3,7 +3,7 @@ module git.nix13.pw/scuroneko/laniakea
go 1.25 go 1.25
require ( require (
git.nix13.pw/scuroneko/slog v1.0.0 git.nix13.pw/scuroneko/slog v1.0.2
github.com/redis/go-redis/v9 v9.17.3 github.com/redis/go-redis/v9 v9.17.3
github.com/vinovest/sqlx v1.7.1 github.com/vinovest/sqlx v1.7.1
go.mongodb.org/mongo-driver/v2 v2.5.0 go.mongodb.org/mongo-driver/v2 v2.5.0

4
go.sum
View File

@@ -1,7 +1,7 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.nix13.pw/scuroneko/slog v1.0.0 h1:PI0YePrmCopjrljUfwCtBIEwNYB+PBgDzPcCXbetpcE= git.nix13.pw/scuroneko/slog v1.0.2 h1:vZyUROygxC2d5FJHUQM/30xFEHY1JT/aweDZXA4rm2g=
git.nix13.pw/scuroneko/slog v1.0.0/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI= git.nix13.pw/scuroneko/slog v1.0.2/go.mod h1:3Qm2wzkR5KjwOponMfG7TcGSDjmYaFqRAmLvSPTuWJI=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=

View File

@@ -57,6 +57,7 @@ func (b *Bot) handleCallback(update *Update, ctx *MsgContext) {
ctx.From = update.CallbackQuery.From ctx.From = update.CallbackQuery.From
ctx.Msg = update.CallbackQuery.Message ctx.Msg = update.CallbackQuery.Message
ctx.CallbackMsgId = update.CallbackQuery.Message.MessageID ctx.CallbackMsgId = update.CallbackQuery.Message.MessageID
ctx.CallbackQueryId = update.CallbackQuery.ID
ctx.Args = data.Args ctx.Args = data.Args
for _, plugin := range b.plugins { for _, plugin := range b.plugins {

View File

@@ -130,7 +130,28 @@ type DeleteMessageP struct {
MessageID int `json:"message_id"` MessageID int `json:"message_id"`
} }
func (b *Bot) DeleteMessage(params *DeleteMessageP) (*Message, error) { func (b *Bot) DeleteMessage(params *DeleteMessageP) (bool, error) {
req := NewRequest[Message]("deleteMessage", params) req := NewRequest[bool]("deleteMessage", params)
return req.Do(b) ok, err := req.Do(b)
if err != nil {
return false, err
}
return *ok, err
}
type AnswerCallbackQueryP struct {
CallbackQueryID string `json:"callback_query_id"`
Text string `json:"text,omitempty"`
ShowAlert bool `json:"show_alert,omitempty"`
URL string `json:"url,omitempty"`
CacheTime int `json:"cache_time,omitempty"`
}
func (b *Bot) AnswerCallbackQuery(params *AnswerCallbackQueryP) (bool, error) {
req := NewRequest[bool]("answerCallbackQuery", params)
ok, err := req.Do(b)
if err != nil {
return false, err
}
return *ok, err
} }

View File

@@ -8,6 +8,7 @@ type MsgContext struct {
Update *Update Update *Update
From *User From *User
CallbackMsgId int CallbackMsgId int
CallbackQueryId string
FromID int FromID int
Prefix string Prefix string
Text string Text string
@@ -151,14 +152,41 @@ func (ctx *MsgContext) CallbackDelete() {
ctx.delete(ctx.CallbackMsgId) ctx.delete(ctx.CallbackMsgId)
} }
func (ctx *MsgContext) Error(err error) { func (ctx *MsgContext) answerCallbackQuery(url, text string, showAlert bool) {
_, sendErr := ctx.Bot.SendMessage(&SendMessageP{ if len(ctx.CallbackQueryId) == 0 {
ChatID: ctx.Msg.Chat.ID, return
Text: fmt.Sprintf(ctx.Bot.errorTemplate, EscapeMarkdown(err.Error())), }
_, err := ctx.Bot.AnswerCallbackQuery(&AnswerCallbackQueryP{
CallbackQueryID: ctx.CallbackQueryId,
Text: text, ShowAlert: showAlert, URL: url,
}) })
if err != nil {
ctx.Bot.logger.Errorln(err) ctx.Bot.logger.Errorln(err)
if sendErr != nil {
ctx.Bot.logger.Errorln(sendErr)
} }
} }
func (ctx *MsgContext) AnswerCbQuery() {
ctx.answerCallbackQuery("", "", false)
}
func (ctx *MsgContext) AnswerCbQueryText(text string) {
ctx.answerCallbackQuery("", text, false)
}
func (ctx *MsgContext) AnswerCbQueryAlert(text string) {
ctx.answerCallbackQuery("", text, true)
}
func (ctx *MsgContext) AnswerCbQueryUrl(u string) {
ctx.answerCallbackQuery(u, "", false)
}
func (ctx *MsgContext) error(err error) {
text := fmt.Sprintf(ctx.Bot.errorTemplate, EscapeMarkdown(err.Error()))
if ctx.CallbackQueryId != "" {
ctx.answerCallbackQuery("", text, false)
} else {
ctx.answer(text, nil)
}
ctx.Bot.logger.Errorln(err)
}
func (ctx *MsgContext) Error(err error) {
ctx.error(err)
}

View File

@@ -45,17 +45,16 @@ func (p *PluginBuilder) UpdateListener(listener CommandExecutor) *PluginBuilder
return p return p
} }
func (p *PluginBuilder) Build() *Plugin { func (p *PluginBuilder) Build() Plugin {
if len(p.commands) == 0 && len(p.payloads) == 0 { if len(p.commands) == 0 && len(p.payloads) == 0 {
log.Println("no command or payloads") log.Println("no command or payloads")
} }
plugin := &Plugin{ return Plugin{
Name: p.name, Name: p.name,
Commands: p.commands, Commands: p.commands,
Payloads: p.payloads, Payloads: p.payloads,
UpdateListener: p.updateListener, UpdateListener: p.updateListener,
} }
return plugin
} }
func (p *Plugin) Execute(cmd string, ctx *MsgContext, dbContext *DatabaseContext) { func (p *Plugin) Execute(cmd string, ctx *MsgContext, dbContext *DatabaseContext) {
@@ -68,13 +67,13 @@ func (p *Plugin) ExecutePayload(payload string, ctx *MsgContext, dbContext *Data
type Middleware struct { type Middleware struct {
Name string Name string
Executor *CommandExecutor Executor CommandExecutor
Order int Order int
Async bool Async bool
} }
type MiddlewareBuilder struct { type MiddlewareBuilder struct {
name string name string
executor *CommandExecutor executor CommandExecutor
order int order int
async bool async bool
} }
@@ -87,7 +86,7 @@ func (m *MiddlewareBuilder) SetName(name string) *MiddlewareBuilder {
return m return m
} }
func (m *MiddlewareBuilder) SetExecutor(executor CommandExecutor) *MiddlewareBuilder { func (m *MiddlewareBuilder) SetExecutor(executor CommandExecutor) *MiddlewareBuilder {
m.executor = &executor m.executor = executor
return m return m
} }
func (m *MiddlewareBuilder) SetOrder(order int) *MiddlewareBuilder { func (m *MiddlewareBuilder) SetOrder(order int) *MiddlewareBuilder {
@@ -98,19 +97,18 @@ func (m *MiddlewareBuilder) SetAsync(async bool) *MiddlewareBuilder {
m.async = async m.async = async
return m return m
} }
func (m *MiddlewareBuilder) Build() *Middleware { func (m *MiddlewareBuilder) Build() Middleware {
return &Middleware{ return Middleware{
Name: m.name, Name: m.name,
Executor: m.executor, Executor: m.executor,
Order: m.order, Order: m.order,
Async: m.async, Async: m.async,
} }
} }
func (m *Middleware) Execute(ctx *MsgContext, db *DatabaseContext) { func (m Middleware) Execute(ctx *MsgContext, db *DatabaseContext) {
exec := *m.Executor
if m.Async { if m.Async {
go exec(ctx, db) go m.Executor(ctx, db)
} else { } else {
exec(ctx, db) m.Execute(ctx, db)
} }
} }

View File

@@ -73,57 +73,3 @@ func EscapeMarkdownV2(s string) string {
} }
return s return s
} }
func GetUnclosedTag(markdown string) string {
// order is important!
var tags = []string{
"```",
"`",
"*",
"_",
}
var currentTag = ""
markdownRunes := []rune(markdown)
var i = 0
outer:
for i < len(markdownRunes) {
// skip escaped characters (only outside tags)
if markdownRunes[i] == '\\' && currentTag == "" {
i += 2
continue
}
if currentTag != "" {
if strings.HasPrefix(string(markdownRunes[i:]), currentTag) {
// turn a tag off
i += len(currentTag)
currentTag = ""
continue
}
} else {
for _, tag := range tags {
if strings.HasPrefix(string(markdownRunes[i:]), tag) {
// turn a tag on
currentTag = tag
i += len(currentTag)
continue outer
}
}
}
i++
}
return currentTag
}
func IsValid(markdown string) bool {
return GetUnclosedTag(markdown) == ""
}
func FixMarkdown(markdown string) string {
tag := GetUnclosedTag(markdown)
if tag == "" {
return markdown
}
return markdown + tag
}

View File

@@ -1,8 +1,8 @@
package laniakea package laniakea
const ( const (
VersionString = "0.3.2" VersionString = "0.3.6"
VersionMajor = 0 VersionMajor = 0
VersionMinor = 3 VersionMinor = 3
VersionPatch = 2 VersionPatch = 6
) )