From 486c0fb7c4fc31bbc80b68e94041268b5c523d62 Mon Sep 17 00:00:00 2001
From: Anony <73958752+anonyindian@users.noreply.github.com>
Date: Sat, 9 Oct 2021 23:10:17 +0530
Subject: [PATCH] spamwatch-go v1
This is the first version of spamwatch-go wrapper.
---
README.md | 64 ++++++++++++
constants.go | 11 +++
errors.go | 32 ++++++
examples/banlist/main.go | 41 ++++++++
examples/misc/main.go | 27 ++++++
examples/tokens/main.go | 49 ++++++++++
go.mod | 3 +
methods.go | 203 +++++++++++++++++++++++++++++++++++++++
requests.go | 55 +++++++++++
types.go | 72 ++++++++++++++
10 files changed, 557 insertions(+)
create mode 100644 README.md
create mode 100644 constants.go
create mode 100644 errors.go
create mode 100644 examples/banlist/main.go
create mode 100644 examples/misc/main.go
create mode 100644 examples/tokens/main.go
create mode 100644 go.mod
create mode 100644 methods.go
create mode 100644 requests.go
create mode 100644 types.go
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7fc70b4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,64 @@
+# SpamWatch API Go Wrapper
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/SpamWatch/spamwatch-go.svg)](https://pkg.go.dev/github.com/SpamWatch/spamwatch-go) [![GPLv3 license](https://img.shields.io/badge/License-GPLv3-blue.svg)](http://perso.crans.org/besson/LICENSE.html)
+
+spamwatch-go is official Go wrapper for [SpamWatch API](https://api.spamwat.ch), which is fast, secure and requires no additional packages to be installed.
+
+
+
+## Features
+
+- Can use custom SpamWatch API endpoint with the help of ClientOpts.
+- It's in pure go, no need to install any kind of plugin or include any kind of additional files.
+- No third party library bloat; only uses standard library.
+- Type safe; no weird `interface{}` logic.
+
+
+
+## Getting started
+
+You can easily download the library with the standard `go get` command:
+
+```bash
+go get github.com/SpamWatch/spamwatch-go
+```
+
+Full documentation of this API, can be found [here](https://docs.spamwat.ch/).
+
+
+
+## Basic Usage
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "github.com/SpamWatch/spamwatch-go"
+)
+
+var client = spamwatch.Client("API_KEY", nil)
+
+func main() {
+ ban, err := client.GetBan(777000)
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Println(ban)
+}
+```
+
+Still need more examples? Take a look at the [examples directory](examples).
+
+Ask your doubts at the [support group](https://telegram.me/SpamWatchSupport).
+
+
+
+## License
+
+[![GNU General Public License v3.0](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl-3.0.en.html#header)
+
+The spamwatch-go project is under the [GPL-3.0](https://opensource.org/licenses/GPL-3.0) license. You can find the license file [here](LICENSE).
+
diff --git a/constants.go b/constants.go
new file mode 100644
index 0000000..4648978
--- /dev/null
+++ b/constants.go
@@ -0,0 +1,11 @@
+package spamwatch
+
+const DEFAULT_API_URL = "https://api.spamwat.ch/"
+
+const SPAMWATCH_ERROR_PREFIX = "SpamWatch-Error"
+
+const UserPermission = "User"
+
+const AdminPermission = "Admin"
+
+const RootPermission = "Root"
diff --git a/errors.go b/errors.go
new file mode 100644
index 0000000..3695933
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,32 @@
+package spamwatch
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+)
+
+func handleError(sw *SpamWatch, s int, b []byte, method string) error {
+ if s != 200 && s != 201 && s != 204 {
+ var e = Error{}
+ err := json.Unmarshal(b, &e)
+ if err != nil {
+ return err
+ }
+ return &ErrorHandler{Err: &e, Spamwatch: sw, Method: method}
+ }
+ return nil
+}
+
+func (e *ErrorHandler) Error() string {
+ switch e.Err.Code {
+ case 401:
+ return fmt.Sprintf("%s: Make sure your API Token is correct", SPAMWATCH_ERROR_PREFIX)
+ case 403:
+ return fmt.Sprintf("%s: Your token's permission '%s' is not high enough", SPAMWATCH_ERROR_PREFIX, e.Spamwatch.Token.Permission)
+ case 429:
+ return fmt.Sprintf("%s: Too Many Requests for method '%s': Try again in %d seconds", SPAMWATCH_ERROR_PREFIX, e.Method, e.Err.Until-time.Now().Unix())
+ default:
+ return fmt.Sprintf("%s: %s: %d-%s", SPAMWATCH_ERROR_PREFIX, e.Method, e.Err.Code, e.Err.Description)
+ }
+}
diff --git a/examples/banlist/main.go b/examples/banlist/main.go
new file mode 100644
index 0000000..ac8b35a
--- /dev/null
+++ b/examples/banlist/main.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/SpamWatch/spamwatch-go"
+)
+
+var client = spamwatch.Client("API_KEY", nil)
+
+func main() {
+ // Getting a specific ban
+ ban, err := client.GetBan(777000)
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Println(ban)
+
+ // Getting all bans
+ bans, err := client.GetBans()
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Println(bans)
+
+ // Getting a list of banned ids
+ bannedIds, err := client.GetBansMin()
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Println(bannedIds)
+
+ // Adding a ban
+ client.AddBan(777000, "reason", "message")
+
+ // Deleting a ban
+ client.DeleteBan(777000)
+}
diff --git a/examples/misc/main.go b/examples/misc/main.go
new file mode 100644
index 0000000..078f87e
--- /dev/null
+++ b/examples/misc/main.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/SpamWatch/spamwatch-go"
+)
+
+var client = spamwatch.Client("API_KEY", nil)
+
+func main() {
+ // Getting the API version
+ v, err := client.Version()
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Println(v)
+
+ // Getting some stats
+ stats, err := client.Stats()
+ if err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Println(stats.TotalBansCount)
+}
diff --git a/examples/tokens/main.go b/examples/tokens/main.go
new file mode 100644
index 0000000..52ecbd1
--- /dev/null
+++ b/examples/tokens/main.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/SpamWatch/spamwatch-go"
+)
+
+var client = spamwatch.Client("API_KEY", nil)
+
+func main() {
+ // Getting your own Token
+ myToken, err := client.GetSelf()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(myToken)
+
+ // Getting all Tokens
+ tokens, err := client.GetTokens()
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(tokens)
+
+ // Getting a specific Token
+ Token, err := client.GetToken(1)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(Token)
+
+ // Getting a Users tokens
+ tokens, err = client.GetUserTokens(777000)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+ fmt.Println(tokens)
+
+ // Creating a Token
+ client.CreateToken(777000, spamwatch.UserPermission)
+
+ // Retiring a specific Token
+ client.DeleteToken(1)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..efb857b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/SpamWatch/spamwatch-go
+
+go 1.17
diff --git a/methods.go b/methods.go
new file mode 100644
index 0000000..e7bdcc4
--- /dev/null
+++ b/methods.go
@@ -0,0 +1,203 @@
+package spamwatch
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+ "strings"
+)
+
+// This returns the major, minor and patch version of the API. This endpoint doesn't need a Authorization Token.
+// https://docs.spamwat.ch/?go#getting-the-api-version
+func (s *SpamWatch) Version() (*Version, error) {
+ b, err := s.MakeRequest(http.MethodGet, "version", nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = &Version{}
+ err = json.Unmarshal(b, a)
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// This returns general stats about the API. Right now this only returns the total ban count.
+// https://docs.spamwat.ch/?go#getting-some-stats
+func (s *SpamWatch) Stats() (*Stats, error) {
+ b, err := s.MakeRequest(http.MethodGet, "stats", nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = &Stats{}
+ err = json.Unmarshal(b, a)
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// Check the ban status of a specific User.
+// https://docs.spamwat.ch/?go#getting-a-specific-ban
+func (s *SpamWatch) GetBan(id int64) (*BanList, error) {
+ b, err := s.MakeRequest(http.MethodGet, fmt.Sprintf("banlist/%d", id), nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = &BanList{}
+ err = json.Unmarshal(b, a)
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// This returns a list of all Bans.
+// https://docs.spamwat.ch/?go#getting-all-bans
+func (s *SpamWatch) GetBans() (*[]BanList, error) {
+ b, err := s.MakeRequest(http.MethodGet, "banlist", nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = []BanList{}
+ err = json.Unmarshal(b, &a)
+ if err != nil {
+ return nil, err
+ }
+ return &a, nil
+}
+
+// This returns a newline seperated list of all Bans. This method currently ignores the Accept header and will always return a newline seperated list. In the future it might return a JSON with the corresponding content type.
+// https://docs.spamwat.ch/?go#getting-a-list-of-banned-ids
+func (s *SpamWatch) GetBansMin() ([]int64, error) {
+ b, err := s.MakeRequest(http.MethodGet, "banlist/all", nil)
+ if err != nil {
+ return nil, err
+ }
+ idstrs := strings.Fields(string(b))
+ a := make([]int64, 0, len(idstrs))
+ for _, x := range idstrs {
+ n, err := strconv.ParseInt(x, 10, 0)
+ if err != nil {
+ return nil, err
+ }
+ a = append(a, n)
+ }
+ return a, nil
+}
+
+// This method can be used for adding a ban.
+// https://docs.spamwat.ch/?go#adding-a-ban
+func (s *SpamWatch) AddBan(id int64, reason string, message string) (bool, error) {
+ _, err := s.MakeRequest(http.MethodPost, "banlist", bytes.NewBuffer([]byte(fmt.Sprintf(`[{"id":%d,"reason":"%s","message":"%s"}]`, id, reason, message))))
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// This method can be used for adding multiple bans at a time.
+// https://docs.spamwat.ch/?go#adding-a-ban
+func (s *SpamWatch) AddBans(toBan []AddBans) (bool, error) {
+ jsonBytes, err := json.Marshal(toBan)
+ if err != nil {
+ return false, err
+ }
+ _, err = s.MakeRequest(http.MethodPost, "banlist", bytes.NewBuffer(jsonBytes))
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// Deleting a ban
+// https://docs.spamwat.ch/?go#deleting-a-ban
+func (s *SpamWatch) DeleteBan(id int64) (bool, error) {
+ _, err := s.MakeRequest(http.MethodDelete, fmt.Sprintf("banlist/%d", id), nil)
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// This returns the Token the request was made with. Useful for checking the permission Level of the token.
+// https://docs.spamwat.ch/?go#getting-your-own-token
+func (s *SpamWatch) GetSelf() (*Tokens, error) {
+ b, err := s.MakeRequest(http.MethodGet, "tokens/self", nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = &Tokens{}
+ err = json.Unmarshal(b, a)
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// This returns a list of all Tokens.
+// https://docs.spamwat.ch/?go#getting-all-tokens
+func (s *SpamWatch) GetTokens() (*[]Tokens, error) {
+ b, err := s.MakeRequest(http.MethodGet, "tokens", nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = []Tokens{}
+ err = json.Unmarshal(b, &a)
+ if err != nil {
+ return nil, err
+ }
+ return &a, nil
+}
+
+// This returns a specific Tokens.
+// https://docs.spamwat.ch/?go#getting-a-specific-token
+func (s *SpamWatch) GetToken(id int) (*Tokens, error) {
+ b, err := s.MakeRequest(http.MethodGet, fmt.Sprintf("tokens/%d", id), nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = &Tokens{}
+ err = json.Unmarshal(b, a)
+ if err != nil {
+ return nil, err
+ }
+ return a, nil
+}
+
+// This returns a list of all tokens associated with the specified user id.
+// https://docs.spamwat.ch/?go#getting-a-users-tokens
+func (s *SpamWatch) GetUserTokens(id int64) (*[]Tokens, error) {
+ b, err := s.MakeRequest(http.MethodGet, fmt.Sprintf("tokens/userid/%d", id), nil)
+ if err != nil {
+ return nil, err
+ }
+ var a = []Tokens{}
+ err = json.Unmarshal(b, &a)
+ if err != nil {
+ return nil, err
+ }
+ return &a, nil
+}
+
+// Creating a Token
+// https://docs.spamwat.ch/?go#creating-a-token
+func (s *SpamWatch) CreateToken(userId int64, permission string) (bool, error) {
+ _, err := s.MakeRequest(http.MethodPost, "tokens", bytes.NewBuffer([]byte(fmt.Sprintf(`{"id":%d,"permission":"%s"}`, userId, permission))))
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// This retires a specific Token. The Token won't be able to make any requests anymore.
+// https://docs.spamwat.ch/?go#retiring-a-specific-token
+func (s *SpamWatch) DeleteToken(tokenId int) (bool, error) {
+ _, err := s.MakeRequest(http.MethodDelete, fmt.Sprintf("tokens/%d", tokenId), nil)
+ if err != nil {
+ return false, err
+ }
+ return true, nil
+}
diff --git a/requests.go b/requests.go
new file mode 100644
index 0000000..a62cc70
--- /dev/null
+++ b/requests.go
@@ -0,0 +1,55 @@
+package spamwatch
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+)
+
+func Client(token string, opts *ClientOpts) *SpamWatch {
+ if opts == nil {
+ opts = &ClientOpts{}
+ }
+ if opts.ApiUrl == "" {
+ opts.ApiUrl = DEFAULT_API_URL
+ }
+ var sw = &SpamWatch{}
+ sw.TimeOut = opts.TimeOut
+ sw.Token.Token = token
+ sw.ApiUrl = opts.ApiUrl
+ // Ignore error on GetSelf method as it would already be handled in succeeding calls
+ t, _ := sw.GetSelf()
+ if t != nil {
+ sw.Token = *t
+ }
+ return sw
+}
+
+func (s *SpamWatch) MakeRequest(http_method string, method string, body io.Reader) ([]byte, error) {
+ r, err := http.NewRequest(http_method, s.ApiUrl+method, body)
+ r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.Token.Token))
+ if body != nil {
+ r.Header.Set("Content-Type", "application/json")
+ }
+ if err != nil {
+ return nil, fmt.Errorf("failed to build %s request to %s: %w", http_method, method, err)
+ }
+ var client = http.Client{
+ Timeout: s.TimeOut,
+ }
+
+ resp, err := client.Do(r)
+ if err != nil {
+ return nil, fmt.Errorf("failed to execute %s request to %s: %w", http_method, method, err)
+ }
+ defer resp.Body.Close()
+ b, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+ err = handleError(s, resp.StatusCode, b, method)
+ if err != nil {
+ return nil, err
+ }
+ return b, nil
+}
diff --git a/types.go b/types.go
new file mode 100644
index 0000000..d0516dd
--- /dev/null
+++ b/types.go
@@ -0,0 +1,72 @@
+package spamwatch
+
+import "time"
+
+type SpamWatch struct {
+ Token Tokens
+ ApiUrl string
+ TimeOut time.Duration
+}
+
+// JSON object returned on /version method
+// https://docs.spamwat.ch/?go#misc
+type Version struct {
+ Major string `json:"major"`
+ Minor string `json:"minor"`
+ Patch string `json:"patch"`
+ Version string `json:"version"`
+}
+
+// JSON object returned on /stats method
+// https://docs.spamwat.ch/?go#misc
+type Stats struct {
+ TotalBansCount int `json:"total_ban_count"`
+}
+
+// JSON object returned on /banlist methods
+// https://docs.spamwat.ch/?go#banlist
+type BanList struct {
+ Admin int `json:"admin,omitempty"`
+ Date int64 `json:"date,omitempty"`
+ Id int64 `json:"id"`
+ Reason string `json:"reason"`
+ Message string `json:"message,omitempty"`
+}
+
+// JSON object used while adding multiple bans
+type AddBans struct {
+ Id int64 `json:"id"`
+ Reason string `json:"reason"`
+ Message string `json:"message,omitempty"`
+}
+
+// JSON object returned in /tokens methods
+// https://docs.spamwat.ch/?go#tokens
+type Tokens struct {
+ Id int `json:"id"`
+ Permission string `json:"permission"`
+ Retired bool `json:"retired"`
+ Token string `json:"token"`
+ Userid int64 `json:"userid"`
+}
+
+// JSON object returned when an error occurs
+type Error struct {
+ Code int `json:"code"`
+ Description string `json:"error"`
+ Until int64 `json:"until,omitempty"`
+ Reason string `json:"reason,omitempty"`
+}
+
+// Struct that handles errors
+type ErrorHandler struct {
+ Err *Error
+ Spamwatch *SpamWatch
+ Method string
+}
+
+// These are the optional parameters of SpamWatch Client
+type ClientOpts struct {
+ ApiUrl string
+ TimeOut time.Duration
+}