feat: add telescope

This commit is contained in:
2026-07-05 08:42:42 +03:30
parent c6997477b9
commit 37baef565e
9 changed files with 530 additions and 8 deletions
+45 -3
View File
@@ -9,10 +9,13 @@ import (
"time"
)
// Kavenegar کلاینت ارسال OTP از طریق verify/lookup (معادل KavenegarService.php).
// Kavenegar کلاینت ارسال OTP. اگر Template تنظیم شده باشد از verify/lookup
// (تمپلیتِ تأییدشده) استفاده می‌کند؛ در غیر این صورت پیامکِ متنیِ ساده با
// sms/send می‌فرستد (مثل approagency — بدون نیاز به تأییدِ تمپلیت).
type Kavenegar struct {
APIKey string
Template string // مثل "loginotp"
Template string // خالی ⇒ ارسالِ متنیِ ساده
Sender string // خطِ فرستنده (خالی ⇒ خطِ پیش‌فرضِ حساب)
client *http.Client
}
@@ -24,11 +27,14 @@ func NewKavenegar(apiKey, template string) *Kavenegar {
}
}
// SendVerify کد را با تمپلیت verify ارسال می‌کند.
// 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)
@@ -59,3 +65,39 @@ func (k *Kavenegar) SendVerify(ctx context.Context, mobile, token string) error
}
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
}