inline keyboard
This commit is contained in:
103
bot.go
103
bot.go
@@ -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"
|
||||||
@@ -66,13 +67,15 @@ func LoadSettingsFromEnv() *BotSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type MsgContext struct {
|
type MsgContext struct {
|
||||||
Bot *Bot
|
Bot *Bot
|
||||||
Msg *Message
|
Msg *Message
|
||||||
Update *Update
|
Update *Update
|
||||||
FromID int
|
From *User
|
||||||
Prefix string
|
CallbackMsgId int
|
||||||
Text string
|
FromID int
|
||||||
Args []string
|
Prefix string
|
||||||
|
Text string
|
||||||
|
Args []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseContext struct {
|
type DatabaseContext struct {
|
||||||
@@ -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,
|
||||||
@@ -420,17 +440,17 @@ 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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// request is a low-level call to api.
|
// request is a low-level call to api.
|
||||||
@@ -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 {
|
||||||
|
|||||||
37
keyboard.go
37
keyboard.go
@@ -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 {
|
||||||
|
|||||||
61
methods.go
61
methods.go
@@ -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 {
|
||||||
@@ -50,20 +61,19 @@ func (b *Bot) GetMe() (*User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SendMessageP struct {
|
type SendMessageP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int `json:"chat_id"`
|
ChatID int `json:"chat_id"`
|
||||||
MessageThreadID int `json:"message_thread_id,omitempty"`
|
MessageThreadID int `json:"message_thread_id,omitempty"`
|
||||||
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
ParseMode ParseMode `json:"parse_mode,omitempty"`
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
Entities []*MessageEntity `json:"entities,omitempty"`
|
Entities []*MessageEntity `json:"entities,omitempty"`
|
||||||
LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"`
|
LinkPreviewOptions *LinkPreviewOptions `json:"link_preview_options,omitempty"`
|
||||||
DisableNotifications bool `json:"disable_notifications,omitempty"`
|
DisableNotifications bool `json:"disable_notifications,omitempty"`
|
||||||
ProtectContent bool `json:"protect_content,omitempty"`
|
ProtectContent bool `json:"protect_content,omitempty"`
|
||||||
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) {
|
||||||
@@ -103,12 +113,13 @@ func (b *Bot) SendPhoto(params *SendPhotoP) (*Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EditMessageTextP struct {
|
type EditMessageTextP struct {
|
||||||
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
BusinessConnectionID string `json:"business_connection_id,omitempty"`
|
||||||
ChatID int `json:"chat_id,omitempty"`
|
ChatID int `json:"chat_id,omitempty"`
|
||||||
MessageID int `json:"message_id,omitempty"`
|
MessageID int `json:"message_id,omitempty"`
|
||||||
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) {
|
||||||
|
|||||||
14
types.go
14
types.go
@@ -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 {
|
||||||
@@ -123,9 +121,9 @@ 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"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user