diff --git a/.gitignore b/.gitignore index e69de29..723ef36 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/err.go b/err.go new file mode 100644 index 0000000..1e79f40 --- /dev/null +++ b/err.go @@ -0,0 +1,32 @@ +package scn + +import ( + "errors" + "fmt" +) + +type ErrorType string + +var ( + ErrAuthFailed ErrorType = "GOEXT_SCN_AUTHFAILED" + ErrQuota ErrorType = "GOEXT_SCN_QUOTAEXCEEDED" + ErrBadRequest ErrorType = "GOEXT_SCN_BADREQUEST" + ErrInternalServerErr ErrorType = "GOEXT_SCN_INTERNALSERVER" + ErrOther ErrorType = "GOEXT_SCN_OTHER" +) + +type InternalError struct { + errType ErrorType + errorMessage string +} + +func NewError(errType ErrorType, errMessage string) *InternalError { + return &InternalError{ + errType: errType, + errorMessage: errMessage, + } +} + +func (e *InternalError) Build() error { + return errors.New(fmt.Sprintf("[%v]: %v", e.errType, e.errorMessage)) +} diff --git a/scn.go b/scn.go new file mode 100644 index 0000000..858054a --- /dev/null +++ b/scn.go @@ -0,0 +1,13 @@ +package scn + +type Connection struct { + uid string + token string +} + +func New(userid string, token string) *Connection { + return &Connection{ + uid: userid, + token: token, + } +} diff --git a/send.go b/send.go new file mode 100644 index 0000000..1d9d8c2 --- /dev/null +++ b/send.go @@ -0,0 +1,161 @@ +package scn + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "time" +) + +type MessageResponse struct { + ErrHighlight int `json:"errhighlight"` + Error int `json:"error"` + IsPro bool `json:"is_pro"` + Message string `json:"message"` + Messagecount int `json:"messagecount"` + Quota int `json:"quota"` + QuotaMax int `json:"quota_max"` + SCNMessageID string `json:"scn_msg_id"` + Success bool `json:"success"` + SuppressSend bool `json:"suppress_send"` +} + +type MessageErrResponse struct { + Errhighlight int `json:"errhighlight"` + Error int `json:"error"` + Message string `json:"message"` + Success bool `json:"success"` +} + +type MessageBuilder struct { + conn *Connection + title string + content *string + channel *string + time *time.Time + sendername *string + priority *int +} + +func (c *Connection) Message(title string) *MessageBuilder { + return &MessageBuilder{conn: c, title: title} +} + +func (c *MessageBuilder) Channel(channel string) *MessageBuilder { + c.channel = &channel + return c +} + +func (c *MessageBuilder) Content(content string) *MessageBuilder { + c.content = &content + return c +} + +func (c *MessageBuilder) Time(t time.Time) *MessageBuilder { + c.time = &t + return c +} + +func (c *MessageBuilder) SenderName(sn string) *MessageBuilder { + c.sendername = &sn + return c +} + +func (c *MessageBuilder) Priority(p int) *MessageBuilder { + c.priority = &p + return c +} + +func (c *MessageBuilder) Send(ctx context.Context) (MessageResponse, error) { + client := http.Client{Timeout: 5 * time.Second} + + body := map[string]interface{}{} + + body["user_id"] = c.conn.uid + body["key"] = c.conn.token + + if c.channel != nil { + body["channel"] = *c.channel + } + + body["title"] = c.title + + if c.content != nil { + body["content"] = *c.content + } + + if c.sendername != nil { + body["content"] = *c.sendername + } + + if c.time != nil { + body["timestamp"] = c.time.Unix() + } + + if c.priority != nil { + body["priority"] = *c.priority + } + + body["msg_id"] = mustHexUUID() + + rawbody, err := json.Marshal(body) + if err != nil { + return MessageResponse{}, err + } + + req, err := http.NewRequestWithContext(ctx, "POST", "https://simplecloudnotifier.de/", bytes.NewBuffer(rawbody)) + if err != nil { + return MessageResponse{}, err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := client.Do(req) + if err != nil { + return MessageResponse{}, err + } + + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode >= 200 && resp.StatusCode < 300 { + + raw, err := io.ReadAll(resp.Body) + if err != nil { + return MessageResponse{}, err + } + + var mr MessageResponse + err = json.Unmarshal(raw, &mr) + if err != nil { + return MessageResponse{}, err + } + + return mr, nil + } else { + errMessage := resp.Status + + if raw, err := io.ReadAll(resp.Body); err == nil { + var mr MessageErrResponse + if err = json.Unmarshal(raw, &mr); err == nil { + errMessage = mr.Message + } + } + + if resp.StatusCode == 400 { + return MessageResponse{}, NewError(ErrBadRequest, errMessage).Build() + } + if resp.StatusCode == 401 { + return MessageResponse{}, NewError(ErrAuthFailed, errMessage).Build() + } + if resp.StatusCode == 403 { + return MessageResponse{}, NewError(ErrQuota, errMessage).Build() + } + if resp.StatusCode == 500 { + return MessageResponse{}, NewError(ErrInternalServerErr, errMessage).Build() + } + + return MessageResponse{}, NewError(ErrOther, errMessage).Build() + } + +} diff --git a/send_test.go b/send_test.go new file mode 100644 index 0000000..a16d61f --- /dev/null +++ b/send_test.go @@ -0,0 +1,24 @@ +package scn + +import ( + "context" + "fmt" + "testing" +) + +func TestSendSCN(t *testing.T) { + t.Skip() + return + + mr, err := New("TODO", "TODO"). + Message("Hello Test"). + Content("This is a test / goext"). + Send(context.Background()) + + if err != nil { + panic(err) + } + + fmt.Printf("%+v\n", mr) + +} diff --git a/uuid.go b/uuid.go new file mode 100644 index 0000000..785b0e3 --- /dev/null +++ b/uuid.go @@ -0,0 +1,49 @@ +package scn + +import ( + "crypto/rand" + "encoding/hex" + "io" +) + +func mustHexUUID() string { + v, err := NewHexUUID() + if err != nil { + panic(err) + } + return v +} + +func NewHexUUID() (string, error) { + uuid, err := NewUUID() + if err != nil { + return "", err + } + + // Result: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + + var dst = make([]byte, 36) + + hex.Encode(dst, uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) + + return string(dst), nil +} + +func NewUUID() ([16]byte, error) { + var uuid [16]byte + _, err := io.ReadFull(rand.Reader, uuid[:]) + if err != nil { + return [16]byte{}, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +}