package auth import ( "context" "encoding/json" "fmt" "net/http" "net/url" "time" ) // Kavenegar کلاینت ارسال OTP. اگر Template تنظیم شده باشد از verify/lookup // (تمپلیتِ تأییدشده) استفاده می‌کند؛ در غیر این صورت پیامکِ متنیِ ساده با // sms/send می‌فرستد (مثل approagency — بدون نیاز به تأییدِ تمپلیت). type Kavenegar struct { APIKey string Template string // خالی ⇒ ارسالِ متنیِ ساده Sender string // خطِ فرستنده (خالی ⇒ خطِ پیش‌فرضِ حساب) client *http.Client } func NewKavenegar(apiKey, template string) *Kavenegar { return &Kavenegar{ APIKey: apiKey, Template: template, client: &http.Client{Timeout: 5 * time.Second}, } } // SendVerify کد ورود را ارسال می‌کند (verify/lookup اگر تمپلیت باشد، وگرنه متنِ ساده). func (k *Kavenegar) SendVerify(ctx context.Context, mobile, token string) error { if k.APIKey == "" { return fmt.Errorf("KAVE_API_KEY is null") } if k.Template == "" { return k.sendPlain(ctx, mobile, token) } endpoint := fmt.Sprintf("https://api.kavenegar.com/v1/%s/verify/lookup.json", k.APIKey) q := url.Values{} q.Set("receptor", mobile) q.Set("token", token) q.Set("template", k.Template) req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint+"?"+q.Encode(), nil) if err != nil { return err } resp, err := k.client.Do(req) if err != nil { return err } defer resp.Body.Close() var out struct { Return struct { Status int `json:"status"` Message string `json:"message"` } `json:"return"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return fmt.Errorf("kavenegar decode: %w", err) } if out.Return.Status != 200 { return fmt.Errorf("kavenegar failed: status=%d msg=%s", out.Return.Status, out.Return.Message) } return nil } // sendPlain پیامکِ متنیِ سادهٔ کد ورود را با sms/send می‌فرستد (بدونِ تمپلیت). // قالبِ پیام مانند approagency است؛ خطِ پایانی «#کد» به تشخیصِ خودکارِ کد کمک می‌کند. func (k *Kavenegar) sendPlain(ctx context.Context, mobile, token string) error { endpoint := fmt.Sprintf("https://api.kavenegar.com/v1/%s/sms/send.json", k.APIKey) message := fmt.Sprintf("کد ورود به حکم‌شو: %s\n#%s", token, token) q := url.Values{} q.Set("receptor", mobile) q.Set("message", message) if k.Sender != "" { q.Set("sender", k.Sender) } req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint+"?"+q.Encode(), nil) if err != nil { return err } resp, err := k.client.Do(req) if err != nil { return err } defer resp.Body.Close() var out struct { Return struct { Status int `json:"status"` Message string `json:"message"` } `json:"return"` } if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { return fmt.Errorf("kavenegar decode: %w", err) } if out.Return.Status != 200 { return fmt.Errorf("kavenegar sms failed: status=%d msg=%s", out.Return.Status, out.Return.Message) } return nil }