inline keyboard

This commit is contained in:
2026-01-22 13:47:28 +03:00
parent ce13b19676
commit 7a3e40a74d
4 changed files with 133 additions and 82 deletions

77
bot.go
View File

@@ -9,6 +9,7 @@ import (
"os" "os"
"sort" "sort"
"strings" "strings"
"time"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"github.com/vinovest/sqlx" "github.com/vinovest/sqlx"
@@ -69,6 +70,8 @@ type MsgContext struct {
Bot *Bot Bot *Bot
Msg *Message Msg *Message
Update *Update Update *Update
From *User
CallbackMsgId int
FromID int FromID int
Prefix string Prefix string
Text string Text string
@@ -221,6 +224,7 @@ func (b *Bot) Run() {
for { for {
queue := b.updateQueue queue := b.updateQueue
if queue.IsEmpty() { if queue.IsEmpty() {
time.Sleep(time.Millisecond * 25)
continue continue
} }
@@ -265,6 +269,7 @@ func (b *Bot) handleMessage(update *Update, ctx *MsgContext) {
} }
ctx.FromID = update.Message.From.ID ctx.FromID = update.Message.From.ID
ctx.From = update.Message.From
ctx.Msg = update.Message ctx.Msg = update.Message
text = strings.TrimSpace(text) text = strings.TrimSpace(text)
prefix, hasPrefix := b.checkPrefixes(text) prefix, hasPrefix := b.checkPrefixes(text)
@@ -298,6 +303,12 @@ func (b *Bot) handleCallback(update *Update, ctx *MsgContext) {
return return
} }
ctx.FromID = update.CallbackQuery.From.ID
ctx.From = update.CallbackQuery.From
ctx.Msg = update.CallbackQuery.Message
ctx.CallbackMsgId = update.CallbackQuery.Message.MessageID
ctx.Args = data.Args
for _, plugin := range b.plugins { for _, plugin := range b.plugins {
_, ok := plugin.Payloads[data.Command] _, ok := plugin.Payloads[data.Command]
if !ok { if !ok {
@@ -325,6 +336,40 @@ type AnswerMessage struct {
ctx *MsgContext ctx *MsgContext
} }
func (ctx *MsgContext) edit(messageId int, text string, keyboard *InlineKeyboard) *AnswerMessage {
params := &EditMessageTextP{
MessageID: messageId,
ChatID: ctx.Msg.Chat.ID,
Text: text,
ParseMode: ParseMD,
}
if keyboard != nil {
params.ReplyMarkup = keyboard.Get()
}
msg, err := ctx.Bot.EditMessageText(params)
if err != nil {
ctx.Bot.logger.Error(err)
return nil
}
return &AnswerMessage{
MessageID: msg.MessageID, ctx: ctx, Text: text, IsMedia: false,
}
}
func (m *AnswerMessage) Edit(text string) *AnswerMessage {
return m.ctx.edit(m.MessageID, text, nil)
}
func (ctx *MsgContext) EditCallback(text string, keyboard *InlineKeyboard) *AnswerMessage {
if ctx.CallbackMsgId == 0 {
ctx.Bot.logger.Error("Can't edit non-callback update message")
return nil
}
return ctx.edit(ctx.CallbackMsgId, text, keyboard)
}
func (ctx *MsgContext) EditCallbackf(format string, keyboard *InlineKeyboard, args ...any) *AnswerMessage {
return ctx.EditCallback(fmt.Sprintf(format, args...), keyboard)
}
func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard) *AnswerMessage { func (ctx *MsgContext) answer(text string, keyboard *InlineKeyboard) *AnswerMessage {
params := &SendMessageP{ params := &SendMessageP{
ChatID: ctx.Msg.Chat.ID, ChatID: ctx.Msg.Chat.ID,
@@ -369,31 +414,6 @@ func (ctx *MsgContext) AnswerPhoto(photoId string, text string) *AnswerMessage {
} }
} }
func (m *AnswerMessage) Edit(text string) *AnswerMessage {
var msg *Message
var err error
if m.IsMedia {
msg, err = m.ctx.Bot.EditMessageCaption(&EditMessageCaptionP{
MessageID: m.MessageID,
ChatID: m.ctx.Msg.Chat.ID,
Caption: text,
ParseMode: ParseMD,
})
} else {
msg, err = m.ctx.Bot.EditMessageText(&EditMessageTextP{
MessageID: m.MessageID,
ChatID: m.ctx.Msg.Chat.ID,
Text: text,
ParseMode: ParseMD,
})
}
if err != nil {
m.ctx.Bot.logger.Error(err)
}
m.MessageID = msg.MessageID
return m
}
func (m *AnswerMessage) Delete() { func (m *AnswerMessage) Delete() {
_, err := m.ctx.Bot.DeleteMessage(&DeleteMessageP{ _, err := m.ctx.Bot.DeleteMessage(&DeleteMessageP{
MessageID: m.MessageID, ChatID: m.ctx.Msg.Chat.ID, MessageID: m.MessageID, ChatID: m.ctx.Msg.Chat.ID,
@@ -421,14 +441,14 @@ func (b *Bot) Logger() *Logger {
type ApiResponse struct { type ApiResponse struct {
Ok bool `json:"ok"` Ok bool `json:"ok"`
Result map[string]interface{} `json:"result,omitempty"` Result map[string]any `json:"result,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
ErrorCode int `json:"error_code,omitempty"` ErrorCode int `json:"error_code,omitempty"`
} }
type ApiResponseA struct { type ApiResponseA struct {
Ok bool `json:"ok"` Ok bool `json:"ok"`
Result []interface{} `json:"result,omitempty"` Result []any `json:"result,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
ErrorCode int `json:"error_code,omitempty"` ErrorCode int `json:"error_code,omitempty"`
} }
@@ -458,9 +478,10 @@ func (b *Bot) request(methodName string, params any) (map[string]interface{}, er
if err != nil { if err != nil {
return nil, err return nil, err
} }
b.requestLogger.Debug(fmt.Sprintf("RES %s %s", methodName, string(data)))
response := new(ApiResponse) response := new(ApiResponse)
var result map[string]interface{} var result map[string]any
err = json.Unmarshal(data, &response) err = json.Unmarshal(data, &response)
if err != nil { if err != nil {

View File

@@ -1,49 +1,70 @@
package laniakea package laniakea
import "encoding/json" import (
"encoding/json"
"fmt"
)
type InlineKeyboard struct { type InlineKeyboard struct {
CurrentLine []InlineKeyboardButton CurrentLine []InlineKeyboardButton
Lines [][]InlineKeyboardButton Lines [][]InlineKeyboardButton
maxRow int
} }
func NewInlineKeyboard() *InlineKeyboard { func NewInlineKeyboard(maxRow int) *InlineKeyboard {
return &InlineKeyboard{ return &InlineKeyboard{
CurrentLine: make([]InlineKeyboardButton, 0), CurrentLine: make([]InlineKeyboardButton, 0),
Lines: make([][]InlineKeyboardButton, 0), Lines: make([][]InlineKeyboardButton, 0),
maxRow: maxRow,
} }
} }
func (in *InlineKeyboard) append(button InlineKeyboardButton) *InlineKeyboard { func (in *InlineKeyboard) append(button InlineKeyboardButton) *InlineKeyboard {
if len(in.CurrentLine) == in.maxRow {
in.AddLine()
}
in.CurrentLine = append(in.CurrentLine, button) in.CurrentLine = append(in.CurrentLine, button)
return in return in
} }
func (in *InlineKeyboard) AddUrlButton(text, url string) *InlineKeyboard { func (in *InlineKeyboard) AddUrlButton(text, url string) *InlineKeyboard {
return in.append(InlineKeyboardButton{Text: text, URL: url}) return in.append(InlineKeyboardButton{Text: text, URL: url})
} }
func (in *InlineKeyboard) AddCallbackButton(text string, data CallbackData) *InlineKeyboard { func (in *InlineKeyboard) AddCallbackButton(text string, cmd string, args ...any) *InlineKeyboard {
return in.append(InlineKeyboardButton{Text: text, CallbackData: data.ToJson()}) return in.append(InlineKeyboardButton{Text: text, CallbackData: NewCallbackData(cmd, args...).ToJson()})
} }
func (in *InlineKeyboard) AddLine() *InlineKeyboard { func (in *InlineKeyboard) AddLine() *InlineKeyboard {
if len(in.CurrentLine) == 0 {
return in
}
in.Lines = append(in.Lines, in.CurrentLine) in.Lines = append(in.Lines, in.CurrentLine)
in.CurrentLine = make([]InlineKeyboardButton, 0) in.CurrentLine = make([]InlineKeyboardButton, 0)
return in return in
} }
func (in *InlineKeyboard) Get() *InlineKeyboardMarkup { func (in *InlineKeyboard) Get() InlineKeyboardMarkup {
if len(in.CurrentLine) > 0 { if len(in.CurrentLine) > 0 {
in.Lines = append(in.Lines, in.CurrentLine) in.Lines = append(in.Lines, in.CurrentLine)
} }
return &InlineKeyboardMarkup{ return InlineKeyboardMarkup{
InlineKeyboard: in.Lines, InlineKeyboard: in.Lines,
} }
} }
type CallbackData struct { type CallbackData struct {
Command string `json:"cmd"` Command string `json:"cmd"`
Args []any `json:"args"` Args []string `json:"args"`
} }
func NewCallbackData(command string, args ...any) *CallbackData {
stringArgs := make([]string, len(args))
for i, arg := range args {
stringArgs[i] = fmt.Sprint(arg)
}
return &CallbackData{
Command: command,
Args: stringArgs,
}
}
func (d *CallbackData) ToJson() string { func (d *CallbackData) ToJson() string {
data, err := json.Marshal(d) data, err := json.Marshal(d)
if err != nil { if err != nil {

View File

@@ -1,11 +1,14 @@
package laniakea package laniakea
import "fmt" import (
"encoding/json"
"fmt"
)
var NoParams = make(map[string]interface{}) var NoParams = make(map[string]any)
func (b *Bot) Updates() ([]*Update, error) { func (b *Bot) Updates() ([]*Update, error) {
params := make(map[string]interface{}) params := make(map[string]any)
params["offset"] = b.updateOffset params["offset"] = b.updateOffset
params["timeout"] = 30 params["timeout"] = 30
params["allowed_updates"] = b.updateTypes params["allowed_updates"] = b.updateTypes
@@ -15,12 +18,20 @@ func (b *Bot) Updates() ([]*Update, error) {
return nil, err return nil, err
} }
res := make([]*Update, 0) res := make([]*Update, 0)
for _, u := range data["data"].([]interface{}) { for _, u := range data["data"].([]any) {
updateObj := new(Update) updateObj := new(Update)
err = MapToStruct(u.(map[string]interface{}), updateObj) data, err := json.Marshal(u)
if err != nil { if err != nil {
return res, err return res, err
} }
err = json.Unmarshal(data, updateObj)
if err != nil {
return res, err
}
//err = MapToStruct(u.(map[string]any), updateObj)
//if err != nil {
// return res, err
//}
b.updateOffset = updateObj.UpdateID + 1 b.updateOffset = updateObj.UpdateID + 1
err = b.updateQueue.Enqueue(updateObj) err = b.updateQueue.Enqueue(updateObj)
if err != nil { if err != nil {
@@ -62,8 +73,7 @@ type SendMessageP struct {
AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"` AllowPaidBroadcast bool `json:"allow_paid_broadcast,omitempty"`
MessageEffectID string `json:"message_effect_id,omitempty"` MessageEffectID string `json:"message_effect_id,omitempty"`
ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"` ReplyParameters *ReplyParameters `json:"reply_parameters,omitempty"`
ReplyMarkup *InlineKeyboardMarkup `json:"reply_markup,omitempty"` ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
// ReplyKeyboardMarkup *ReplyKeyboardMarkup `json:"reply_markup,omitempty"`
} }
func (b *Bot) SendMessage(params *SendMessageP) (*Message, error) { func (b *Bot) SendMessage(params *SendMessageP) (*Message, error) {
@@ -109,6 +119,7 @@ type EditMessageTextP struct {
InlineMessageID string `json:"inline_message_id,omitempty"` InlineMessageID string `json:"inline_message_id,omitempty"`
Text string `json:"text"` Text string `json:"text"`
ParseMode ParseMode `json:"parse_mode,omitempty"` ParseMode ParseMode `json:"parse_mode,omitempty"`
ReplyMarkup InlineKeyboardMarkup `json:"reply_markup,omitempty"`
} }
func (b *Bot) EditMessageText(params *EditMessageTextP) (*Message, error) { func (b *Bot) EditMessageText(params *EditMessageTextP) (*Message, error) {

View File

@@ -12,9 +12,9 @@ type Update struct {
DeletedBusinessMessage *Message `json:"deleted_business_messages,omitempty"` DeletedBusinessMessage *Message `json:"deleted_business_messages,omitempty"`
MessageReaction *MessageReactionUpdated `json:"message_reaction,omitempty"` MessageReaction *MessageReactionUpdated `json:"message_reaction,omitempty"`
MessageReactionCount *MessageReactionCountUpdated `json:"message_reaction_count,omitempty"` MessageReactionCount *MessageReactionCountUpdated `json:"message_reaction_count,omitempty"`
CallbackQuery *CallbackQuery `json:"callback_query,omitempty"`
InlineQuery int InlineQuery int
ChosenInlineResult int ChosenInlineResult int
CallbackQuery *CallbackQuery `json:"callback_query,omitempty"`
} }
type User struct { type User struct {
@@ -44,7 +44,7 @@ type Chat struct {
} }
type MessageReplyMarkup struct { type MessageReplyMarkup struct {
InlineKeyboard *InlineKeyboardMarkup `json:"inline_keyboard"` InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"`
} }
type Message struct { type Message struct {
@@ -68,8 +68,6 @@ type InaccessableMessage struct {
} }
type MaybeInaccessibleMessage struct { type MaybeInaccessibleMessage struct {
Message
InaccessableMessage
} }
type MessageEntity struct { type MessageEntity struct {
@@ -109,7 +107,7 @@ type LinkPreviewOptions struct {
} }
type InlineKeyboardMarkup struct { type InlineKeyboardMarkup struct {
InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"` InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard,omitempty"`
} }
type InlineKeyboardButton struct { type InlineKeyboardButton struct {
@@ -124,8 +122,8 @@ type ReplyKeyboardMarkup struct {
type CallbackQuery struct { type CallbackQuery struct {
ID string `json:"id"` ID string `json:"id"`
From *User `json:"user"` From *User `json:"from"`
Message *MaybeInaccessibleMessage `json:"message"` Message *Message `json:"message"`
Data string `json:"data"` Data string `json:"data"`
} }