summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/SevereCloud/vksdk/v2/api/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/SevereCloud/vksdk/v2/api/api.go')
-rw-r--r--vendor/github.com/SevereCloud/vksdk/v2/api/api.go359
1 files changed, 359 insertions, 0 deletions
diff --git a/vendor/github.com/SevereCloud/vksdk/v2/api/api.go b/vendor/github.com/SevereCloud/vksdk/v2/api/api.go
new file mode 100644
index 00000000..56da7784
--- /dev/null
+++ b/vendor/github.com/SevereCloud/vksdk/v2/api/api.go
@@ -0,0 +1,359 @@
+/*
+Package api implements VK API.
+
+See more https://vk.com/dev/api_requests
+*/
+package api // import "github.com/SevereCloud/vksdk/v2/api"
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "mime"
+ "net/http"
+ "net/url"
+ "reflect"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/SevereCloud/vksdk/v2"
+ "github.com/SevereCloud/vksdk/v2/internal"
+ "github.com/SevereCloud/vksdk/v2/object"
+)
+
+// Api constants.
+const (
+ Version = vksdk.API
+ MethodURL = "https://api.vk.com/method/"
+)
+
+// VKontakte API methods (except for methods from secure and ads sections)
+// with user access key or service access key can be accessed
+// no more than 3 times per second. The community access key is limited
+// to 20 requests per second.
+//
+// Maximum amount of calls to the secure section methods depends
+// on the app's users amount. If an app has less than
+// 10 000 users, 5 requests per second,
+// up to 100 000 – 8 requests,
+// up to 1 000 000 – 20 requests,
+// 1 000 000+ – 35 requests.
+//
+// The ads section methods are subject to their own limitations,
+// you can read them on this page - https://vk.com/dev/ads_limits
+//
+// If one of this limits is exceeded, the server will return following error:
+// "Too many requests per second". (errors.TooMany).
+//
+// If your app's logic implies many requests in a row, check the execute method.
+// It allows for up to 25 requests for different methods in a single request.
+//
+// In addition to restrictions on the frequency of calls, there are also
+// quantitative restrictions on calling the same type of methods.
+//
+// After exceeding the quantitative limit, access to a particular method may
+// require entering a captcha (see https://vk.com/dev/captcha_error),
+// and may also be temporarily restricted (in this case, the server does
+// not return a response to the call of a particular method, but handles
+// any other requests without problems).
+//
+// If this error occurs, the following parameters are also passed in
+// the error message:
+//
+// CaptchaSID - identifier captcha.
+//
+// CaptchaImg - a link to the image that you want to show the user
+// to enter text from that image.
+//
+// In this case, you should ask the user to enter text from
+// the CaptchaImg image and repeat the request by adding parameters to it:
+//
+// captcha_sid - the obtained identifier;
+//
+// captcha_key - text entered by the user.
+//
+// More info: https://vk.com/dev/api_requests
+const (
+ LimitUserToken = 3
+ LimitGroupToken = 20
+)
+
+// VK struct.
+type VK struct {
+ accessTokens []string
+ lastToken uint32
+ MethodURL string
+ Version string
+ Client *http.Client
+ Limit int
+ UserAgent string
+ Handler func(method string, params ...Params) (Response, error)
+
+ mux sync.Mutex
+ lastTime time.Time
+ rps int
+}
+
+// Response struct.
+type Response struct {
+ Response json.RawMessage `json:"response"`
+ Error Error `json:"error"`
+ ExecuteErrors ExecuteErrors `json:"execute_errors"`
+}
+
+// NewVK returns a new VK.
+//
+// The VKSDK will use the http.DefaultClient.
+// This means that if the http.DefaultClient is modified by other components
+// of your application the modifications will be picked up by the SDK as well.
+//
+// In some cases this might be intended, but it is a better practice
+// to create a custom HTTP Client to share explicitly through
+// your application. You can configure the VKSDK to use the custom
+// HTTP Client by setting the VK.Client value.
+//
+// This set limit 20 requests per second for one token.
+func NewVK(tokens ...string) *VK {
+ var vk VK
+
+ vk.accessTokens = tokens
+ vk.Version = Version
+
+ vk.Handler = vk.defaultHandler
+
+ vk.MethodURL = MethodURL
+ vk.Client = http.DefaultClient
+ vk.Limit = LimitGroupToken
+ vk.UserAgent = internal.UserAgent
+
+ return &vk
+}
+
+// getToken return next token (simple round-robin).
+func (vk *VK) getToken() string {
+ i := atomic.AddUint32(&vk.lastToken, 1)
+ return vk.accessTokens[(int(i)-1)%len(vk.accessTokens)]
+}
+
+// Params type.
+type Params map[string]interface{}
+
+// Lang - determines the language for the data to be displayed on. For
+// example country and city names. If you use a non-cyrillic language,
+// cyrillic symbols will be transliterated automatically.
+// Numeric format from account.getInfo is supported as well.
+//
+// p.Lang(object.LangRU)
+//
+// See all language code in module object.
+func (p Params) Lang(v int) Params {
+ p["lang"] = v
+ return p
+}
+
+// TestMode allows to send requests from a native app without switching it on
+// for all users.
+func (p Params) TestMode(v bool) Params {
+ p["test_mode"] = v
+ return p
+}
+
+// CaptchaSID received ID.
+//
+// See https://vk.com/dev/captcha_error
+func (p Params) CaptchaSID(v string) Params {
+ p["captcha_sid"] = v
+ return p
+}
+
+// CaptchaKey text input.
+//
+// See https://vk.com/dev/captcha_error
+func (p Params) CaptchaKey(v string) Params {
+ p["captcha_key"] = v
+ return p
+}
+
+// Confirm parameter.
+//
+// See https://vk.com/dev/need_confirmation
+func (p Params) Confirm(v bool) Params {
+ p["confirm"] = v
+ return p
+}
+
+// WithContext parameter.
+func (p Params) WithContext(ctx context.Context) Params {
+ p[":context"] = ctx
+ return p
+}
+
+func buildQuery(sliceParams ...Params) (context.Context, url.Values) {
+ query := url.Values{}
+ ctx := context.Background()
+
+ for _, params := range sliceParams {
+ for key, value := range params {
+ if key != ":context" {
+ query.Set(key, FmtValue(value, 0))
+ } else {
+ ctx = value.(context.Context)
+ }
+ }
+ }
+
+ return ctx, query
+}
+
+// defaultHandler provides access to VK API methods.
+func (vk *VK) defaultHandler(method string, sliceParams ...Params) (Response, error) {
+ u := vk.MethodURL + method
+ ctx, query := buildQuery(sliceParams...)
+ attempt := 0
+
+ for {
+ var response Response
+
+ attempt++
+
+ // Rate limiting
+ if vk.Limit > 0 {
+ vk.mux.Lock()
+
+ sleepTime := time.Second - time.Since(vk.lastTime)
+ if sleepTime < 0 {
+ vk.lastTime = time.Now()
+ vk.rps = 0
+ } else if vk.rps == vk.Limit*len(vk.accessTokens) {
+ time.Sleep(sleepTime)
+ vk.lastTime = time.Now()
+ vk.rps = 0
+ }
+ vk.rps++
+
+ vk.mux.Unlock()
+ }
+
+ rawBody := bytes.NewBufferString(query.Encode())
+
+ req, err := http.NewRequestWithContext(ctx, "POST", u, rawBody)
+ if err != nil {
+ return response, err
+ }
+
+ req.Header.Set("User-Agent", vk.UserAgent)
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ resp, err := vk.Client.Do(req)
+ if err != nil {
+ return response, err
+ }
+
+ mediatype, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
+ if mediatype != "application/json" {
+ _ = resp.Body.Close()
+ return response, &InvalidContentType{mediatype}
+ }
+
+ err = json.NewDecoder(resp.Body).Decode(&response)
+ if err != nil {
+ _ = resp.Body.Close()
+ return response, err
+ }
+
+ _ = resp.Body.Close()
+
+ switch response.Error.Code {
+ case ErrNoType:
+ return response, nil
+ case ErrTooMany:
+ if attempt < vk.Limit {
+ continue
+ }
+
+ return response, &response.Error
+ }
+
+ return response, &response.Error
+ }
+}
+
+// Request provides access to VK API methods.
+func (vk *VK) Request(method string, sliceParams ...Params) ([]byte, error) {
+ token := vk.getToken()
+
+ reqParams := Params{
+ "access_token": token,
+ "v": vk.Version,
+ }
+
+ sliceParams = append(sliceParams, reqParams)
+
+ resp, err := vk.Handler(method, sliceParams...)
+
+ return resp.Response, err
+}
+
+// RequestUnmarshal provides access to VK API methods.
+func (vk *VK) RequestUnmarshal(method string, obj interface{}, sliceParams ...Params) error {
+ rawResponse, err := vk.Request(method, sliceParams...)
+ if err != nil {
+ return err
+ }
+
+ return json.Unmarshal(rawResponse, &obj)
+}
+
+func fmtReflectValue(value reflect.Value, depth int) string {
+ switch f := value; value.Kind() {
+ case reflect.Invalid:
+ return ""
+ case reflect.Bool:
+ return fmtBool(f.Bool())
+ case reflect.Array, reflect.Slice:
+ s := ""
+
+ for i := 0; i < f.Len(); i++ {
+ if i > 0 {
+ s += ","
+ }
+
+ s += FmtValue(f.Index(i).Interface(), depth)
+ }
+
+ return s
+ case reflect.Ptr:
+ // pointer to array or slice or struct? ok at top level
+ // but not embedded (avoid loops)
+ if depth == 0 && f.Pointer() != 0 {
+ switch a := f.Elem(); a.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Struct, reflect.Map:
+ return FmtValue(a.Interface(), depth+1)
+ }
+ }
+ }
+
+ return fmt.Sprint(value)
+}
+
+// FmtValue return vk format string.
+func FmtValue(value interface{}, depth int) string {
+ if value == nil {
+ return ""
+ }
+
+ switch f := value.(type) {
+ case bool:
+ return fmtBool(f)
+ case object.Attachment:
+ return f.ToAttachment()
+ case object.JSONObject:
+ return f.ToJSON()
+ case reflect.Value:
+ return fmtReflectValue(f, depth)
+ }
+
+ return fmtReflectValue(reflect.ValueOf(value), depth)
+}