refactor(misc): share the renderer POST and placeholder helpers

This commit is contained in:
tiennm99 committed 2026-10-01 13:02:46 +07:00
1 parent 97d8aeaadf
commit 53ca1e59b3
2 files changed
+19 -12

No files matched your search

@@ -89,11 +89,18 @@ func (c wheelAPIClient) Render(ctx context.Context, options []string, winner int
return nil, fmt.Errorf("wheelofnames api request encode failed: %w", err)
}
return c.post(ctx, endpoint, body, "image/gif", isWheelGIF)
}
// post sends one JSON render request to endpoint and returns the response
// body once it is a 2xx of the expected media type that passes isValid. The
// gif and gacha renderers share it because they live on the same service.
func (c wheelAPIClient) post(ctx context.Context, endpoint *url.URL, body []byte, mediaType string, isValid func([]byte) bool) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), bytes.NewReader(body))
if err != nil {
return nil, fmt.Errorf("wheelofnames api request build failed: %w", err)
}
req.Header.Set("Accept", "image/gif")
req.Header.Set("Accept", mediaType)
req.Header.Set("Content-Type", "application/json")
if c.Token != "" {
req.Header.Set("Authorization", "Bearer "+c.Token)
@@ -108,7 +115,7 @@ func (c wheelAPIClient) Render(ctx context.Context, options []string, winner int
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
return nil, fmt.Errorf("wheelofnames api status %d", resp.StatusCode)
}
if err := requireWheelGIFContentType(resp.Header.Get("Content-Type")); err != nil {
if err := requireContentType(resp.Header.Get("Content-Type"), mediaType); err != nil {
return nil, err
}
@@ -122,8 +129,8 @@ func (c wheelAPIClient) Render(ctx context.Context, options []string, winner int
if len(data) == 0 {
return nil, fmt.Errorf("wheelofnames api response empty")
}
if !isWheelGIF(data) {
return nil, fmt.Errorf("wheelofnames api response is not a gif")
if !isValid(data) {
return nil, fmt.Errorf("wheelofnames api response is not %s", mediaType)
}
return data, nil
}
@@ -150,9 +157,9 @@ func wheelAPIEndpoint(rawURL string) (*url.URL, error) {
return endpoint, nil
}
func requireWheelGIFContentType(contentType string) error {
func requireContentType(contentType, want string) error {
mediaType, _, err := mime.ParseMediaType(contentType)
if err != nil || mediaType != "image/gif" {
if err != nil || mediaType != want {
return fmt.Errorf("wheelofnames api content type %q unsupported", contentType)
}
return nil
@@ -35,7 +35,7 @@ func wheelOfNamesCommand() modules.Command {
return chathelper.Reply(ctx, b, update.Message, wheelUsage)
}
winner := pickWheelOption(options)
placeholder := sendWheelPlaceholder(ctx, b, update.Message)
placeholder := sendRenderPlaceholder(ctx, b, update.Message, wheelPlaceholder)
animation, err := renderWheelOfNamesAnimation(ctx, options, winner)
if err != nil {
if !errors.Is(err, errWheelAPINotConfigured) {
@@ -74,17 +74,17 @@ const wheelUsage = "Usage: /wheelofnames <option,...>"
const wheelPlaceholder = "Spinning..."
// sendWheelPlaceholder posts the "Spinning..." holding message and returns its
// id, or 0 when none was posted.
// sendRenderPlaceholder posts a holding message such as "Spinning..." and
// returns its id, or 0 when none was posted.
//
// It is skipped unless a renderer is configured: without one the winner reply
// is immediate, and a placeholder would only flash. A failed placeholder is
// non-fatal — the spin still resolves, just without the holding message.
func sendWheelPlaceholder(ctx context.Context, b *bot.Bot, msg *models.Message) int {
// non-fatal — the pick still resolves, just without the holding message.
func sendRenderPlaceholder(ctx context.Context, b *bot.Bot, msg *models.Message, text string) int {
if _, err := wheelAPIEndpoint(newWheelAPIClientFromEnv().URL); err != nil {
return 0
}
id, err := chathelper.SendText(ctx, b, msg, wheelPlaceholder)
id, err := chathelper.SendText(ctx, b, msg, text)
if err != nil {
log.Warn("wheelofnames placeholder send failed", "chat", msg.Chat.ID, "err", err)
return 0