Skip to content

Commit

Permalink
tgb: CallbackDataFilter and CallbackDataCodec (#140)
Browse files Browse the repository at this point in the history
* feat: add callback data encode/decode

* examples: CallbackDataParser

* add MustButton method

* remove url.JoinPath

* tests: CallbackDataFilter

* lint: ignore some lines in tests
  • Loading branch information
mr-linch authored Mar 13, 2024
1 parent 4e3fcdc commit ab4b6bb
Show file tree
Hide file tree
Showing 5 changed files with 1,420 additions and 33 deletions.
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ Listen port set to `:8000` by default, you can override it with the `-webhook-li
| [text-filter](https://github.com/mr-linch/go-tg/tree/main/examples/text-filter) | Text Filter usage | Text filter, reply keyboard markup |
| [webapps](https://github.com/mr-linch/go-tg/tree/main/examples/webapps) | Parse and validate Login Widget & WebApp data, host simple webapp | WebApps, Login Widget, Embed webhook to http.Mux |
| [session-filter](https://github.com/mr-linch/go-tg/tree/main/examples/session-filter) | Simple form filling with persistent session | Router, Session Manager, Session Filters |
| [menu](https://github.com/mr-linch/go-tg/tree/main/examples/menu) | Hiearchical menu with API integration | ButtonLayout, TextMessageBuilder, CallbackDataFilter |
| [retry-flood](https://github.com/mr-linch/go-tg/tree/main/examples/retry-flood) | Retry on flood error | Interceptors |
148 changes: 148 additions & 0 deletions examples/menu/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package main

import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
)

// Primitive API client for https://jsonplaceholder.typicode.com
type API struct {
BaseURL string
Client *http.Client
}

type User struct {
ID int `json:"id"`
Name string `json:"name"`
Username string `json:"username"`
Email string `json:"email"`
Address struct {
Street string `json:"street"`
Suite string `json:"suite"`
City string `json:"city"`
Zipcode string `json:"zipcode"`
Geo struct {
Lat float64 `json:"lat,string"`
Lng float64 `json:"lng,string"`
} `json:"geo"`
} `json:"address"`
Phone string `json:"phone"`
Website string `json:"website"`
Company struct {
Name string `json:"name"`
CatchPhrase string `json:"catchPhrase"`
Bs string `json:"bs"`
} `json:"company"`
}

type Post struct {
UserID int `json:"userId"`
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
}

type Comment struct {
PostID int `json:"postId"`
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Body string `json:"body"`
}

func (a *API) request(ctx context.Context, path string, params url.Values, dst any) error {
// better use url.JoinPath, but it's not available at go1.18 that specified as minimal version
endpoint := a.BaseURL + path

if len(params) > 0 {
endpoinAsURL, err := url.Parse(endpoint)
if err != nil {
return fmt.Errorf("parse endpoint: %w", err)
}

endpoinAsURL.RawQuery = params.Encode()

endpoint = endpoinAsURL.String()
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, http.NoBody)
if err != nil {
return fmt.Errorf("build http request: %w", err)
}

res, err := a.Client.Do(req)
if err != nil {
return fmt.Errorf("execute http request: %w", err)
}
defer res.Body.Close()

if res.StatusCode != http.StatusOK {
return fmt.Errorf("http status: %d", res.StatusCode)
}

if err := json.NewDecoder(res.Body).Decode(dst); err != nil {
return fmt.Errorf("unmarshal: %w", err)
}

return nil
}

func (a *API) Users(ctx context.Context) (users []User, err error) {
err = a.request(ctx, "/users", nil, &users)
return
}

func (a *API) User(ctx context.Context, id int) (user User, err error) {
err = a.request(ctx, fmt.Sprintf("/users/%d", id), nil, &user)
return
}

type PostsParams struct {
UserID int `json:"userId"`
}

func (a *API) Posts(ctx context.Context, params *PostsParams) (posts []Post, err error) {
vs := url.Values{}

if params != nil {
if params.UserID != 0 {
vs.Set("userId", strconv.Itoa(params.UserID))
}
}

err = a.request(ctx, "/posts", vs, &posts)

return
}

func (a *API) Post(ctx context.Context, id int) (post Post, err error) {
err = a.request(ctx, fmt.Sprintf("/posts/%d", id), nil, &post)
return
}

type CommentsParams struct {
PostID int `json:"postId"`
}

func (a *API) Comments(ctx context.Context, params *CommentsParams) (comments []Comment, err error) {
vs := url.Values{}

if params != nil {
if params.PostID != 0 {
vs.Set("postId", strconv.Itoa(params.PostID))
}
}

err = a.request(ctx, "/comments", vs, &comments)

return
}

func (a *API) Comment(ctx context.Context, id int) (comment Comment, err error) {
err = a.request(ctx, fmt.Sprintf("/comments/%d", id), nil, &comment)
return
}
Loading

0 comments on commit ab4b6bb

Please sign in to comment.