in progress
This commit is contained in:
5
.vscode/extensions.json
vendored
Normal file
5
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"gigacode.gigacode-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
112
README.md
112
README.md
@@ -1,3 +1,111 @@
|
|||||||
# smsc
|
# SMSc - Отправка SMS через smsc.ru
|
||||||
|
|
||||||
Отправка смс при помощи сервиса smsc.ru
|
Пакет `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
|
||||||
|
|||||||
66
request.go
Normal file
66
request.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
10
response.go
Normal file
10
response.go
Normal file
@@ -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 == ""
|
||||||
|
}
|
||||||
24
sms.go
Normal file
24
sms.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
49
sms_test.go
Normal file
49
sms_test.go
Normal file
@@ -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))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user