Files
aura-crm/production/backend/internal/clientnotify/vk.go
T

61 lines
1.9 KiB
Go

package clientnotify
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"net/url"
"strconv"
"strings"
)
// vkBaseURL — VK's public API, no self-hosting concept like Telegram's,
// same reasoning as MAX's own hardcoded maxBaseURL.
const vkBaseURL = "https://api.vk.com/method"
const vkAPIVersion = "5.199"
// buildVkSendRequest — unlike Telegram/MAX's JSON bodies, messages.send
// takes form-encoded params (verified against VK's current API docs) and
// always answers HTTP 200 even on failure, with the error nested in the
// body under "error" — see parseVkSendResponse.
func buildVkSendRequest(ctx context.Context, text, groupToken, chatID string) (*http.Request, error) {
form := url.Values{
"user_id": {chatID},
"message": {text},
// VK's random_id is a 32-bit signed int in practice — a nanosecond
// epoch value (~19 digits) overflows that and gets every send
// rejected with VK error 100 ("invalid parameter"). rand.Int31()
// stays within the accepted range.
"random_id": {strconv.FormatInt(int64(rand.Int31()), 10)},
"access_token": {groupToken},
"v": {vkAPIVersion},
}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, vkBaseURL+"/messages.send", strings.NewReader(form.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
return req, nil
}
type vkSendError struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
}
func parseVkSendResponse(body []byte) (string, error) {
var out struct {
Response json.Number `json:"response"`
Error *vkSendError `json:"error"`
}
if err := json.Unmarshal(body, &out); err != nil {
return "", fmt.Errorf("vk: malformed response: %w", err)
}
if out.Error != nil {
return "", fmt.Errorf("vk: %d: %s", out.Error.ErrorCode, out.Error.ErrorMsg)
}
return out.Response.String(), nil
}