v1.0.0 beta 15

This commit is contained in:
2026-03-12 17:45:53 +03:00
parent 3b6bb82e04
commit d6e2daec04
28 changed files with 1224 additions and 126 deletions

View File

@@ -11,7 +11,6 @@
package laniakea
import (
"encoding/json"
"fmt"
"git.nix13.pw/scuroneko/extypes"
@@ -34,7 +33,7 @@ const (
// - SetIconCustomEmojiId() — adds a custom emoji icon
// - SetStyle() — sets visual style (danger/success/primary)
// - SetUrl() — makes button open a URL
// - SetCallbackData() — attaches structured command + args for bot handling
// - SetCallbackDataJson() — attaches structured command + args for bot handling
//
// Call build() to produce the final tgapi.InlineKeyboardButton.
// Builder methods are immutable — each returns a copy.
@@ -74,18 +73,26 @@ func (b InlineKbButtonBuilder) SetUrl(url string) InlineKbButtonBuilder {
return b
}
// SetCallbackData sets a structured callback payload that will be sent to the bot
// SetCallbackDataJson sets a structured callback payload that will be sent to the bot
// when the button is pressed. The command and arguments are serialized as JSON.
//
// Args are converted to strings using fmt.Sprint. Non-string types (e.g., int, bool)
// are safely serialized, but complex structs may not serialize usefully.
//
// Example: SetCallbackData("delete_user", 123, "confirm") → {"cmd":"delete_user","args":["123","confirm"]}
func (b InlineKbButtonBuilder) SetCallbackData(cmd string, args ...any) InlineKbButtonBuilder {
// Example: SetCallbackDataJson("delete_user", 123, "confirm") → {"cmd":"delete_user","args":["123","confirm"]}
func (b InlineKbButtonBuilder) SetCallbackDataJson(cmd string, args ...any) InlineKbButtonBuilder {
b.callbackData = NewCallbackData(cmd, args...).ToJson()
return b
}
// SetCallbackDataBase64 sets a structured callback payload encoded as Base64.
// This can be useful when the JSON payload exceeds Telegram's callback data length limit.
// Args are converted to strings using fmt.Sprint.
func (b InlineKbButtonBuilder) SetCallbackDataBase64(cmd string, args ...any) InlineKbButtonBuilder {
b.callbackData = NewCallbackData(cmd, args...).ToBase64()
return b
}
// build converts the builder state into a tgapi.InlineKeyboardButton.
// This method is typically called internally by InlineKeyboard.AddButton().
func (b InlineKbButtonBuilder) build() tgapi.InlineKeyboardButton {
@@ -108,6 +115,8 @@ type InlineKeyboard struct {
CurrentLine extypes.Slice[tgapi.InlineKeyboardButton] // Current row being built
Lines [][]tgapi.InlineKeyboardButton // Completed rows
maxRow int // Max buttons per row (e.g., 3 or 4)
payloadType BotPayloadType // Serialization format for callback data (JSON or Base64)
}
// NewInlineKeyboard creates a new keyboard builder with the specified maximum
@@ -119,9 +128,18 @@ func NewInlineKeyboard(maxRow int) *InlineKeyboard {
CurrentLine: make(extypes.Slice[tgapi.InlineKeyboardButton], 0),
Lines: make([][]tgapi.InlineKeyboardButton, 0),
maxRow: maxRow,
payloadType: BotPayloadBase64,
}
}
// SetPayloadType sets the serialization format for callback data added via
// AddCallbackButton and AddCallbackButtonStyle methods.
// It should be one of BotPayloadJson or BotPayloadBase64.
func (in *InlineKeyboard) SetPayloadType(t BotPayloadType) *InlineKeyboard {
in.payloadType = t
return in
}
// append adds a button to the current line. If the line is full, it auto-flushes.
// This is an internal helper used by other builder methods.
func (in *InlineKeyboard) append(button tgapi.InlineKeyboardButton) *InlineKeyboard {
@@ -145,11 +163,11 @@ func (in *InlineKeyboard) AddUrlButtonStyle(text string, style tgapi.KeyboardBut
}
// AddCallbackButton adds a button that sends a structured callback payload to the bot.
// The command and args are serialized as JSON using NewCallbackData.
// The command and args are serialized according to the current payloadType.
func (in *InlineKeyboard) AddCallbackButton(text string, cmd string, args ...any) *InlineKeyboard {
return in.append(tgapi.InlineKeyboardButton{
Text: text,
CallbackData: NewCallbackData(cmd, args...).ToJson(),
CallbackData: NewCallbackData(cmd, args...).Encode(in.payloadType),
})
}
@@ -159,7 +177,7 @@ func (in *InlineKeyboard) AddCallbackButtonStyle(text string, style tgapi.Keyboa
return in.append(tgapi.InlineKeyboardButton{
Text: text,
Style: style,
CallbackData: NewCallbackData(cmd, args...).ToJson(),
CallbackData: NewCallbackData(cmd, args...).Encode(in.payloadType),
})
}
@@ -230,10 +248,33 @@ func NewCallbackData(command string, args ...any) *CallbackData {
// This fallback ensures the bot receives a valid JSON payload even if internal
// errors occur — avoiding "invalid callback_data" errors from Telegram.
func (d *CallbackData) ToJson() string {
data, err := json.Marshal(d)
data, err := encodeJsonPayload(*d)
if err != nil {
// Fallback: return minimal valid JSON to avoid Telegram API rejection
return `{"cmd":""}`
}
return string(data)
return data
}
// ToBase64 serializes the CallbackData to a JSON string and then encodes it as Base64.
// Returns an empty string if serialization or encoding fails.
func (d *CallbackData) ToBase64() string {
s, err := encodeBase64Payload(*d)
if err != nil {
return ``
}
return s
}
// Encode serializes the CallbackData according to the specified payload type.
// Supported types: BotPayloadJson and BotPayloadBase64.
// For unknown types, returns an empty string.
func (d *CallbackData) Encode(t BotPayloadType) string {
switch t {
case BotPayloadBase64:
return d.ToBase64()
case BotPayloadJson:
return d.ToJson()
}
return ""
}