From 69ec3a6050c086f2077c20bf43739658ccf6eeb7 Mon Sep 17 00:00:00 2001 From: Maksimov V Vladimir Date: Sat, 31 Jan 2026 19:30:41 +0300 Subject: [PATCH] in progress --- .vscode/extensions.json | 5 ++ README.md | 112 +++++++++++++++++++++++++++++++++++++++- go.mod | 3 ++ request.go | 66 +++++++++++++++++++++++ response.go | 10 ++++ sms.go | 24 +++++++++ sms_test.go | 49 ++++++++++++++++++ 7 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 go.mod create mode 100644 request.go create mode 100644 response.go create mode 100644 sms.go create mode 100644 sms_test.go diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a899cad --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "gigacode.gigacode-vscode" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 46c3a72..7aaa802 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,111 @@ -# smsc +# SMSc - Отправка SMS через smsc.ru -Отправка смс при помощи сервиса smsc.ru \ No newline at end of file +Пакет `smsc` предоставляет простой способ отправки SMS через сервис [smsc.ru](https://smsc.ru) на языке Go. + +## Установка + +```bash +go get git.gm6.ru/icewind/smsc +``` + +## Использование + +```go +package main + +import ( + "git.gm6.ru/icewind/smsc" +) + +func main() { + // Создание запроса на отправку SMS + req := smsc.RequestSMS{ + ApiKey: "ваш_api_ключ", // Замените на ваш реальный API-ключ + Phone: "+79991234567", // Номер получателя + Message: "Привет от Go!", // Текст сообщения + } + + // Отправка SMS + resp := smsc.SendSMS(req) + + // Проверка результата + if resp.Error != "" { + // Обработка ошибки + panic(resp.Error) + } + + // Успешная отправка + fmt.Printf("SMS отправлена. ID: %d, Количество сообщений: %d\n", resp.ID, resp.Cnt) +} +``` + +## Функции + +### `SendSMS(req RequestSMS) ResponseSMS` +Отправляет SMS через сервис smsc.ru. + +**Параметры:** +- `req` — структура `RequestSMS` с данными для отправки. + +**Возвращает:** +- `ResponseSMS` — ответ от сервера, включая `ID` и `Cnt`, или ошибку. + +## Структуры + +### `RequestSMS` + +| Поле | Тип | Описание | JSON-тег | +|-----------|--------|------------------------------|--------------| +| `ApiKey` | string | API-ключ от smsc.ru | `apikey` | +| `Phone` | string | Номер телефона получателя | `phones` | +| `Message` | string | Текст сообщения | `mes` | + +### `ResponseSMS` + +| Поле | Тип | Описание | JSON-тег | +|-----------|-----|---------------------------------|----------| +| `ID` | int | Уникальный идентификатор SMS | `id` | +| `Cnt` | int | Количество отправленных SMS | `cnt` | +| `Error` | string | Описание ошибки (если есть) | `error` | +| `ErrorCode`| int | Код ошибки | `error_code` | + +## Обработка ошибок + +Если произошла ошибка при отправке, поле `Error` в `ResponseSMS` будет содержать описание ошибки. + +```go +resp := smsc.SendSMS(req) +if resp.Error != "" { + log.Printf("Ошибка: %s (код: %d)\n", resp.Error, resp.ErrorCode) +} +``` + +## Тестирование + +Для тестирования функции `SendSMS` можно использовать реальные вызовы (интеграционные тесты). Пример теста: + +```go +func TestSendSMS(t *testing.T) { + req := RequestSMS{ + ApiKey: "ваш_api_ключ", + Phone: "+79991234567", + Message: "Тестовое сообщение", + } + + resp := SendSMS(req) + + if resp.Error != "" { + t.Errorf("Ошибка при отправке SMS: %s", resp.Error) + } + + if resp.ID == 0 { + t.Error("Ожидался ненулевой ID") + } +} +``` + +> **Внимание:** Не забудьте заменить `ваш_api_ключ` на реальный ключ и убедитесь, что номер телефона корректен. + +## Лицензия + +MIT diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6478229 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.gm6.ru/icewind/smsc + +go 1.25.4 diff --git a/request.go b/request.go new file mode 100644 index 0000000..5037598 --- /dev/null +++ b/request.go @@ -0,0 +1,66 @@ +package smsc + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" +) + +func httpRequest(req *http.Request, resp any) error { + client := &http.Client{} + + httpResp, err := client.Do(req) + if err != nil { + return fmt.Errorf("http request error: %w", err) + } + defer httpResp.Body.Close() + + body, err := io.ReadAll(httpResp.Body) + if err != nil { + return fmt.Errorf("http response read error: %w", err) + } + + //log.Println(string(body)) + + if err = json.Unmarshal(body, resp); err != nil { + var rErr Response + if err = json.Unmarshal(body, &rErr); err == nil && rErr.ErrorCode != 0 { + return fmt.Errorf("error %d: %s", rErr.ErrorCode, rErr.Error) + } + err = fmt.Errorf("http response unmarshal error: %w", err) + } + return err +} + +func requestPost(url string, req, resp any) error { + reqData, err := json.Marshal(req) + if err != nil { + return fmt.Errorf("request marshal error: %w", err) + } + + httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqData)) + if err != nil { + return fmt.Errorf("http request create error: %w", err) + } + + httpReq.Header.Set("Content-Type", "application/json") + httpReq.Header.Set("Accept", "application/json") + + err = httpRequest(httpReq, resp) + return err +} + +func requestGet(url, apiKey string, params url.Values, resp any) error { + httpReq, err := http.NewRequest("GET", url, nil) + if err != nil { + return fmt.Errorf("http request create error: %w", err) + } + httpReq.URL.RawQuery = params.Encode() + httpReq.Header.Set("Accept", "application/json") + + err = httpRequest(httpReq, resp) + return err +} diff --git a/response.go b/response.go new file mode 100644 index 0000000..ca8e7b2 --- /dev/null +++ b/response.go @@ -0,0 +1,10 @@ +package smsc + +type Response struct { + Error string `json:"error"` + ErrorCode int `json:"error_code"` +} + +func (s *Response) IsError() bool { + return s.Error == "" +} diff --git a/sms.go b/sms.go new file mode 100644 index 0000000..c47ce06 --- /dev/null +++ b/sms.go @@ -0,0 +1,24 @@ +package smsc + +const ( + UrlSMS = "https://smsc.ru/rest/send/" +) + +type RequestSMS struct { + ApiKey string `json:"apikey"` + Phone string `json:"phones"` + Message string `json:"mes"` +} + +type ResponseSMS struct { + Response + ID int `json:"id"` + Cnt int `json:"cnt"` +} + +func SendSMS(req RequestSMS) (resp ResponseSMS) { + if err := requestPost(UrlSMS, req, &resp); err != nil { + resp.Error = err.Error() + } + return +} diff --git a/sms_test.go b/sms_test.go new file mode 100644 index 0000000..60d495b --- /dev/null +++ b/sms_test.go @@ -0,0 +1,49 @@ +package smsc + +import ( + "encoding/json" + "fmt" + "math/rand" + "testing" +) + +func generateCode() string { + return fmt.Sprintf("%04d", rand.Intn(10000)) +} + +func TestSendSMS(t *testing.T) { + // Создаем тестовый запрос + req := RequestSMS{ + ApiKey: "80g8w5-t4D5n2X5v2!8s1S3p5E2n2X68e", // Замените на реальный API-ключ для теста + Phone: "79397804368", // Замените на реальный номер телефона + Message: "Код подтверждения: " + generateCode(), + } + + // Вызываем тестируемую функцию + resp := SendSMS(req) + + // Проверяем, что нет ошибки + if resp.Error != "" { + t.Errorf("Ошибка при отправке SMS: %s", resp.Error) + } + + // Проверяем, что ID и Cnt имеют ожидаемые значения (если применимо) + // Эти проверки могут быть опциональными, так как значения зависят от ответа сервера + if resp.ID == 0 { + t.Error("Ожидался ненулевой ID") + } + if resp.Cnt == 0 { + t.Error("Ожидался ненулевой Cnt") + } + + printJSON(t, resp) +} + +func printJSON(t *testing.T, data any) { + jsonData, err := json.MarshalIndent(data, "", " ") + if err != nil { + t.Errorf("Ошибка при преобразовании в JSON: %v", err) + return + } + fmt.Println(string(jsonData)) +}