REPOSITORY / ScuroNeko/Laniakea
Pull Requests
v1.0.0 #9
@@ -14,6 +14,7 @@
|
||||
- Added `MsgContext.UpsertKeyboard(...)` and `MsgContext.UpsertKeyboardMarkdown(...)` helpers that edit callback messages, replace photo callback messages with a fresh chat message, and send a new chat message outside callback flow.
|
||||
- Added `CommandGroup`, `NewCommandGroup(...)`, `Plugin.CommandGroup(...)`, and `Plugin.AddCommandGroup(...)` helpers for registering prefixed command groups with shared middleware.
|
||||
- Added the `tgfmt` package with typed MarkdownV2, HTML, legacy Markdown formatting helpers, and a message entity builder.
|
||||
- Added `InlineKeyboardButtonBuilder.SetPayloadType(...)`, `InlineKeyboardButtonBuilder.SetCallbackData(...)`, and `MsgContext.NewInlineKeyboardButton(...)` helpers for payload-aware button building.
|
||||
|
||||
### Changed
|
||||
- Version metadata now reports the stable `v1.0.0` release instead of `v1.0.0-rc.16`.
|
||||
@@ -31,6 +32,7 @@
|
||||
- Added regression coverage for inline callback keyboard upserts and callback target detection.
|
||||
- Added regression coverage for command group prefixing, middleware order, clone behavior, and plugin registration.
|
||||
- Added formatting coverage for escaping, composition, link destinations, HTML attributes, and legacy Markdown code blocks.
|
||||
- Added regression coverage for context-aware inline keyboard button payload encoding.
|
||||
|
||||
## v1.0.0-rc.16
|
||||
|
||||
|
||||
+31
-11
@@ -27,11 +27,14 @@ const (
|
||||
// Call build() to produce the final tgapi.InlineKeyboardButton.
|
||||
// Builder methods are immutable — each returns a copy.
|
||||
type InlineKeyboardButtonBuilder struct {
|
||||
text string
|
||||
iconCustomEmojiID string
|
||||
style tgapi.KeyboardButtonStyle
|
||||
url string
|
||||
callbackData string
|
||||
text string
|
||||
emojiID string
|
||||
style tgapi.KeyboardButtonStyle
|
||||
|
||||
url string
|
||||
data string
|
||||
|
||||
payloadType BotPayloadType
|
||||
}
|
||||
|
||||
// NewInlineKeyboardButton creates a new button builder with the given display text.
|
||||
@@ -43,7 +46,7 @@ func NewInlineKeyboardButton(text string) InlineKeyboardButtonBuilder {
|
||||
// SetIconCustomEmojiID sets a custom emoji ID to display as the button's icon.
|
||||
// This is a Telegram Bot API feature for custom emoji icons.
|
||||
func (b InlineKeyboardButtonBuilder) SetIconCustomEmojiID(id string) InlineKeyboardButtonBuilder {
|
||||
b.iconCustomEmojiID = id
|
||||
b.emojiID = id
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -62,6 +65,12 @@ func (b InlineKeyboardButtonBuilder) SetURL(url string) InlineKeyboardButtonBuil
|
||||
return b
|
||||
}
|
||||
|
||||
// SetPayloadType sets the encoding used by SetCallbackData.
|
||||
func (b InlineKeyboardButtonBuilder) SetPayloadType(t BotPayloadType) InlineKeyboardButtonBuilder {
|
||||
b.payloadType = t
|
||||
return b
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
@@ -70,7 +79,7 @@ func (b InlineKeyboardButtonBuilder) SetURL(url string) InlineKeyboardButtonBuil
|
||||
//
|
||||
// Example: SetCallbackDataJSON("delete_user", 123, "confirm") → {"cmd":"delete_user","args":["123","confirm"]}.
|
||||
func (b InlineKeyboardButtonBuilder) SetCallbackDataJSON(cmd string, args ...any) InlineKeyboardButtonBuilder {
|
||||
b.callbackData = NewCallbackData(cmd, args...).ToJSON()
|
||||
b.data = NewCallbackData(cmd, args...).ToJSON()
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -78,7 +87,18 @@ func (b InlineKeyboardButtonBuilder) SetCallbackDataJSON(cmd string, args ...any
|
||||
// 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 InlineKeyboardButtonBuilder) SetCallbackDataBase64(cmd string, args ...any) InlineKeyboardButtonBuilder {
|
||||
b.callbackData = NewCallbackData(cmd, args...).ToBase64()
|
||||
b.data = NewCallbackData(cmd, args...).ToBase64()
|
||||
return b
|
||||
}
|
||||
|
||||
// SetCallbackData sets a structured callback payload using the configured payload type.
|
||||
// The default payload type is JSON.
|
||||
func (b InlineKeyboardButtonBuilder) SetCallbackData(cmd string, args ...any) InlineKeyboardButtonBuilder {
|
||||
if b.payloadType == BotPayloadBase64 {
|
||||
b.data = NewCallbackData(cmd, args...).ToBase64()
|
||||
} else {
|
||||
b.data = NewCallbackData(cmd, args...).ToJSON()
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -88,8 +108,8 @@ func (b InlineKeyboardButtonBuilder) build() tgapi.InlineKeyboardButton {
|
||||
Text: b.text,
|
||||
URL: b.url,
|
||||
Style: b.style,
|
||||
IconCustomEmojiID: b.iconCustomEmojiID,
|
||||
CallbackData: b.callbackData,
|
||||
IconCustomEmojiID: b.emojiID,
|
||||
CallbackData: b.data,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +197,7 @@ 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 according to the current payloadType.
|
||||
func (in *InlineKeyboard) AddCallbackButton(text string, cmd string, args ...any) *InlineKeyboard {
|
||||
func (in *InlineKeyboard) AddCallbackButton(text, cmd string, args ...any) *InlineKeyboard {
|
||||
return in.append(tgapi.InlineKeyboardButton{
|
||||
Text: text,
|
||||
CallbackData: NewCallbackData(cmd, args...).Encode(in.payloadType),
|
||||
|
||||
@@ -45,6 +45,34 @@ func TestInlineKeyboardBuilderPreservesConfiguredButtonFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineKeyboardButtonBuilderSetCallbackDataDefaultsToJSON(t *testing.T) {
|
||||
kb := NewInlineKeyboardBase64(1).
|
||||
AddButton(NewInlineKeyboardButton("A").SetCallbackData("cmd", 1, "two"))
|
||||
|
||||
button := kb.Get().InlineKeyboard[0][0]
|
||||
if !strings.Contains(button.CallbackData, `"cmd":"cmd"`) {
|
||||
t.Fatalf("expected JSON callback payload, got %q", button.CallbackData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineKeyboardButtonBuilderSetCallbackDataUsesConfiguredPayloadType(t *testing.T) {
|
||||
kb := NewInlineKeyboardJSON(1).
|
||||
AddButton(NewInlineKeyboardButton("A").
|
||||
SetPayloadType(BotPayloadBase64).
|
||||
SetCallbackData("cmd", 1, "two"),
|
||||
)
|
||||
|
||||
got, _, err := decodePayload(BotPayloadJSON, kb.Get().InlineKeyboard[0][0].CallbackData, false)
|
||||
if err != nil {
|
||||
t.Fatalf("decodePayload returned error: %v", err)
|
||||
}
|
||||
|
||||
want := CallbackData{Command: "cmd", Args: []string{"1", "two"}}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected payload: got %#v want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInlineKeyboardGetPayloadTypeReturnsLocalOverride(t *testing.T) {
|
||||
kb := NewInlineKeyboardJSON(2)
|
||||
if got := kb.GetPayloadType(); got != BotPayloadJSON {
|
||||
|
||||
@@ -594,6 +594,11 @@ func (ctx *MsgContext) NewInlineKeyboard(maxRow int) *InlineKeyboard {
|
||||
return NewInlineKeyboard(ctx.payloadType, maxRow)
|
||||
}
|
||||
|
||||
// NewInlineKeyboardButton creates a button builder using the context payload encoding.
|
||||
func (ctx *MsgContext) NewInlineKeyboardButton(text string) InlineKeyboardButtonBuilder {
|
||||
return NewInlineKeyboardButton(text).SetPayloadType(ctx.payloadType)
|
||||
}
|
||||
|
||||
func bindPositional(args []string, dst any) error {
|
||||
v := reflect.ValueOf(dst)
|
||||
if v.Kind() != reflect.Pointer || v.IsNil() {
|
||||
|
||||
@@ -91,6 +91,23 @@ func TestBindArgsBindsScalarFields(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewInlineKeyboardButtonUsesContextPayloadType(t *testing.T) {
|
||||
ctx := &MsgContext{payloadType: BotPayloadBase64}
|
||||
|
||||
kb := NewInlineKeyboardJSON(1).
|
||||
AddButton(ctx.NewInlineKeyboardButton("A").SetCallbackData("cmd", 1, "two"))
|
||||
|
||||
got, _, err := decodePayload(BotPayloadJSON, kb.Get().InlineKeyboard[0][0].CallbackData, false)
|
||||
if err != nil {
|
||||
t.Fatalf("decodePayload returned error: %v", err)
|
||||
}
|
||||
|
||||
want := CallbackData{Command: "cmd", Args: []string{"1", "two"}}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("unexpected payload: got %#v want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBindArgsLeavesTrailingFieldsZeroWhenArgsRunOut(t *testing.T) {
|
||||
type input struct {
|
||||
ID int
|
||||
|
||||
Reference in New Issue
Block a user