diff --git a/.golangci.yml b/.golangci.yml index ef5c99c..42e2361 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,6 @@ linters: enable: - - golint + - revive +issues: + exclude: + - "Error return value of .* is not checked" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d74804..a083450 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,18 @@ repos: - id: check-merge-conflict exclude: ^vendor/ - id: detect-private-key +- repo: https://github.com/golangci/golangci-lint + rev: v1.49.0 + hooks: + - id: golangci-lint + exclude: ^vendor/ - repo: https://github.com/hadolint/hadolint rev: v2.10.0 hooks: - id: hadolint + exclude: ^vendor/ +- repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.0 + hooks: + - id: go-fmt + exclude: ^vendor/ diff --git a/Dockerfile b/Dockerfile index 3fb402b..3213e58 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,7 @@ RUN go build -o /usr/local/bin/tilty main.go FROM alpine:3.16 LABEL maintainer="3vilpenguin@gmail.com" +# hadolint ignore=DL3018 RUN apk add -U --no-cache bluez COPY --from=builder /usr/local/bin/tilty /usr/local/bin/tilty diff --git a/README.md b/README.md index f9084a7..1b981e9 100644 --- a/README.md +++ b/README.md @@ -46,40 +46,46 @@ logfile = /var/log/foo.log # defaults to stdout # SQLite example [sqlite] +enabled = true file = /etc/tilty/tilt.sqlite # Generic application/json example [webhook] -url = http://www.foo.com -headers = {"Content-Type": "application/json"} -template = {"color": "{{ color }}", "gravity": {{ gravity }}, "mac": "{{ mac }}", "temp": {{ temp }}, "timestamp": "{{ timestamp }}"} -method = POST +enabled = true +url = "http://www.foo.com" +headers = "{\"Content-Type\": \"application/json\"}" +template = "{\"color\": \"{{.Color}}\", \"gravity\": {{.Gravity}}, \"mac\": \"{{.Mac}}\", \"temp\": {{.Temp}}, \"timestamp\": \"{{.Timestamp}}\", \"gravity_unit\": \"G\", \"temp_unit\": \"F\"}" +method = "POST" # Brewstat.us example [webhook] -url = https://www.brewstat.us/tilt/0yjRbGd2/log -headers = {"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"} -template = {"Color": "{{ color }}", "SG": {{ gravity }}, "Temp": {{ temp }}, "Timepoint": "{{ timestamp }}"} -method = POST +enabled = true +url = "https://www.brewstat.us/tilt/0yjRbGd2/log" +headers = "{\"Content-Type\": \"application/json\"}" +template = "{\"Color\": \"{{.Color}}\", \"SG\": {{.Gravity}}, \"Temp\": {{.Temp}}, \"Timepoint\": \"{{.Timestamp}}\"}" +method = "POST" # Brewers Friend example [webhook] -url = https://log.brewersfriend.com/tilt/3009ec67c6d81276185c90824951bd32bg -headers = {"Content-Type": "application/x-www-form-urlencoded"} -template = {"SG": {{ gravity }}, "Temp": {{ temp }}, "Color": "{{ color }}"} -method = POST +enabled = true +url = "https://log.brewersfriend.com/tilt/3009ec67c6d81276185c90824951bd32bg" +headers = "{\"Content-Type\": \"application/json\"}" +template = "{\"SG\": \"{{.Gravity}}\", \"Temp\": {{.Temp}}, \"Color\": {{.Color}}}" +method = "POST" # Brewfather custom stream example [webhook] -url = https://log.brewfather.net/stream?id=aTHF9WlXKrAb1C -headers = {"Content-Type": "application/json"} -template = {"name": "Tilt {{ color }}", "gravity": {{ gravity }}, "gravity_unit": "G", "temp": {{ temp }}, "temp_unit": "F"} -method = POST +enabled = true +url = "https://log.brewfather.net/stream?id=aTHF9WlXKrAb1C" +headers = "{\"Content-Type\": \"application/json\"}" +template = "{\"name\": \"Tilt {{.Color}}\", \"gravity\": {{.Gravity}}, \"gravity_unit\": \"G\", \"temp\": {{.Temp}}, \"temp_unit\": \"F\"}" +method = "POST" [datadog] +enabled = true # Note: make sure that the dd agent has DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true -host = statsdhost.corp.com -port = 8125 +statsd_host = "statsdhost.corp.com" +statsd_port = 8125 ``` diff --git a/emitters/datadog.go b/emitters/datadog.go index a98f13a..c345ef9 100644 --- a/emitters/datadog.go +++ b/emitters/datadog.go @@ -4,8 +4,7 @@ import ( "encoding/json" "fmt" "github.com/DataDog/datadog-go/v5/statsd" - "github.com/go-kit/kit/log/level" - _ "github.com/mattn/go-sqlite3" + "github.com/go-kit/log/level" "github.com/myoung34/tilty/tilt" ) @@ -15,7 +14,7 @@ type Datadog struct { StatsdPort int `json:"statsd_port"` } -func DatadogEmitWithClient(payload tilt.TiltPayload, emitterConfig interface{}, client statsd.ClientInterface) (string, error) { +func DatadogEmitWithClient(payload tilt.Payload, emitterConfig interface{}, client statsd.ClientInterface) (string, error) { defer client.Close() @@ -39,7 +38,7 @@ func DatadogEmitWithClient(payload tilt.TiltPayload, emitterConfig interface{}, return "", nil } -func DatadogEmit(payload tilt.TiltPayload, emitterConfig interface{}) (string, error) { +func DatadogEmit(payload tilt.Payload, emitterConfig interface{}) (string, error) { datadog := Datadog{} jsonString, _ := json.Marshal(emitterConfig) json.Unmarshal(jsonString, &datadog) diff --git a/emitters/datadog_test.go b/emitters/datadog_test.go index e0d7af4..760e7f4 100644 --- a/emitters/datadog_test.go +++ b/emitters/datadog_test.go @@ -15,14 +15,14 @@ func TestDatadogEmit(t *testing.T) { sampleConfig.ConfigData.Set("datadog.statsd_host", "testing") sampleConfig.ConfigData.Set("datadog.statsd_port", "8125") - payload := tilt.TiltPayload{ - Id: "0987654321", + payload := tilt.Payload{ + ID: "0987654321", Mac: "66:77:88:99:00", Color: "BLACK", Major: 65, Minor: 1098, Rssi: -7, - Timestamp: "2019-11-10 23:59:00 +0000 UTC", + Timestamp: 1661445284, } resp, err := DatadogEmitWithClient(payload, sampleConfig.ConfigData.Get("datadog"), &statsd.NoOpClient{}) assert.Equal(t, nil, err) diff --git a/emitters/models.go b/emitters/models.go index 565ac63..648865b 100644 --- a/emitters/models.go +++ b/emitters/models.go @@ -5,5 +5,5 @@ type Template struct { Gravity string Mac string Temp string - Timestamp string + Timestamp int64 } diff --git a/emitters/sqlite.go b/emitters/sqlite.go index a55fb1a..67ee3d7 100644 --- a/emitters/sqlite.go +++ b/emitters/sqlite.go @@ -4,8 +4,8 @@ import ( "database/sql" "encoding/json" "fmt" - "github.com/go-kit/kit/log/level" - _ "github.com/mattn/go-sqlite3" + "github.com/go-kit/log/level" + _ "github.com/mattn/go-sqlite3" // Per docs "github.com/myoung34/tilty/tilt" "log" ) @@ -15,7 +15,7 @@ type SQLite struct { File string } -func SQLiteEmit(payload tilt.TiltPayload, emitterConfig interface{}) (string, error) { +func SQLiteEmit(payload tilt.Payload, emitterConfig interface{}) (string, error) { sqlite := SQLite{} jsonString, _ := json.Marshal(emitterConfig) json.Unmarshal(jsonString, &sqlite) diff --git a/emitters/sqlite_test.go b/emitters/sqlite_test.go index 279649c..3d56ab0 100644 --- a/emitters/sqlite_test.go +++ b/emitters/sqlite_test.go @@ -15,14 +15,14 @@ func TestSQLite(t *testing.T) { sampleConfig.ConfigData.Set("sqlite.enabled", true) sampleConfig.ConfigData.Set("sqlite.file", "foo.db") - payload := tilt.TiltPayload{ - Id: "0987654321", + payload := tilt.Payload{ + ID: "0987654321", Mac: "66:77:88:99:00", Color: "BLACK", Major: 65, Minor: 1098, Rssi: -7, - Timestamp: "2019-11-10 23:59:00 +0000 UTC", + Timestamp: 1661445284, } resp, err := SQLiteEmit(payload, sampleConfig.ConfigData.Get("sqlite")) assert.Equal(t, nil, err) diff --git a/emitters/webhook.go b/emitters/webhook.go index fd0fa92..93148aa 100644 --- a/emitters/webhook.go +++ b/emitters/webhook.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/json" "fmt" - "github.com/go-kit/kit/log/level" + "github.com/go-kit/log/level" "github.com/myoung34/tilty/tilt" "io" "net/http" @@ -14,18 +14,18 @@ import ( type Webhook struct { Enabled bool - Url string + URL string Headers string Template string Method string } -func WebhookEmit(payload tilt.TiltPayload, emitterConfig interface{}) (string, error) { +func WebhookEmit(payload tilt.Payload, emitterConfig interface{}) (string, error) { webhook := Webhook{} jsonString, _ := json.Marshal(emitterConfig) json.Unmarshal(jsonString, &webhook) - level.Info(tilt.Logger).Log("emitters.webhook", fmt.Sprintf("%s", webhook.Url)) + level.Info(tilt.Logger).Log("emitters.webhook", webhook.URL) level.Info(tilt.Logger).Log("emitters.webhook", fmt.Sprintf("%v", webhook.Enabled)) level.Info(tilt.Logger).Log("emitters.webhook", fmt.Sprintf("%+v", webhook.Headers)) level.Info(tilt.Logger).Log("emitters.webhook", fmt.Sprintf("%+v", webhook.Template)) @@ -43,7 +43,10 @@ func WebhookEmit(payload tilt.TiltPayload, emitterConfig interface{}) (string, e } level.Info(tilt.Logger).Log("emitters.webhook", fmt.Sprintf("%+v", payload)) - tmpl, err := template.New("test").Parse(`{"name": "Tilt {{.Color}}", "gravity": {{.Gravity}}, "gravity_unit": "G", "temp": {{.Temp}}, "temp_unit": "F"}`) + tmpl, err := template.New("webhook").Parse(`{"name": "Tilt {{.Color}}", "gravity": {{.Gravity}}, "gravity_unit": "G", "temp": {{.Temp}}, "temp_unit": "F"}`) + if len(webhook.Template) > 0 { + tmpl, err = template.New("webhook").Parse(webhook.Template) + } if err != nil { level.Error(tilt.Logger).Log("emitters.webhook", err) return "", err @@ -56,7 +59,7 @@ func WebhookEmit(payload tilt.TiltPayload, emitterConfig interface{}) (string, e bodyReader := bytes.NewReader(tpl.Bytes()) // Set up the request - req, err := http.NewRequest(webhook.Method, webhook.Url, bodyReader) + req, err := http.NewRequest(webhook.Method, webhook.URL, bodyReader) if err != nil { level.Error(tilt.Logger).Log("emitters.webhook", err) return "", err diff --git a/emitters/webhook_test.go b/emitters/webhook_test.go index d378ba4..547c377 100644 --- a/emitters/webhook_test.go +++ b/emitters/webhook_test.go @@ -11,9 +11,9 @@ import ( type TiltWebhookTest struct { Type string - Payload tilt.TiltPayload + Payload tilt.Payload Enabled bool - Url string + URL string Headers string Template string Method string @@ -43,23 +43,23 @@ func TestWebhook(t *testing.T) { name: "POST", in: TiltWebhookTest{ Type: "webhook", - Payload: tilt.TiltPayload{ - Id: "1234567890", + Payload: tilt.Payload{ + ID: "1234567890", Mac: "11:22:33:44:55", Color: "RED", Major: 90, Minor: 1024, Rssi: -67, - Timestamp: "2009-11-10 23:00:00 +0000 UTC", + Timestamp: 1661445284, }, Enabled: true, - Url: "http://something.com", + URL: "http://something.com", Headers: "{\"Content-Type\": \"application/json\", \"Foo\": \"bar\"}", - Template: "{\"color\": \"{{ color }}\", \"gravity\": {{ gravity }}, \"mac\": \"{{ mac }}\", \"temp\": {{ temp }}, \"timestamp\": \"{{ timestamp }}\"}", + Template: "{\"color\": \"{{.Color}}\", \"gravity\": {{.Gravity}}, \"mac\": \"{{.Mac}}\", \"temp\": {{.Temp}}, \"timestamp\": \"{{.Timestamp}}\", \"gravity_unit\": \"G\", \"temp_unit\": \"F\"}", Method: "POST", }, out: TiltTest{ - Response: "{\"Response\":\"{\\\"name\\\": \\\"Tilt RED\\\", \\\"gravity\\\": 1024, \\\"gravity_unit\\\": \\\"G\\\", \\\"temp\\\": 90, \\\"temp_unit\\\": \\\"F\\\"}\"}", + Response: "{\"Response\":\"{\\\"color\\\": \\\"RED\\\", \\\"gravity\\\": 1024, \\\"mac\\\": \\\"11:22:33:44:55\\\", \\\"temp\\\": 90, \\\"timestamp\\\": \\\"1661445284\\\", \\\"gravity_unit\\\": \\\"G\\\", \\\"temp_unit\\\": \\\"F\\\"}\"}", CallCount: 1, CallSignature: "POST http://something.com", }, @@ -68,23 +68,23 @@ func TestWebhook(t *testing.T) { name: "GET", in: TiltWebhookTest{ Type: "webhook", - Payload: tilt.TiltPayload{ - Id: "0987654321", + Payload: tilt.Payload{ + ID: "0987654321", Mac: "66:77:88:99:00", Color: "BLACK", Major: 65, Minor: 1098, Rssi: -7, - Timestamp: "2019-11-10 23:59:00 +0000 UTC", + Timestamp: 1661445284, }, Enabled: true, - Url: "http://fake.com", + URL: "http://fake.com", Headers: "{\"Content-Type\": \"application/json\"}", - Template: "{\"color\": \"{{ color }}\", \"gravity\": {{ gravity }}, \"mac\": \"{{ mac }}\", \"temp\": {{ temp }}, \"timestamp\": \"{{ timestamp }}\"}", + Template: "{\"color\": \"{{.Color}}\", \"gravity\": {{.Gravity}}, \"mac\": \"{{.Mac}}\", \"temp\": {{.Temp}}, \"timestamp\": \"{{.Timestamp}}\", \"gravity_unit\": \"G\", \"temp_unit\": \"F\"}", Method: "GET", }, out: TiltTest{ - Response: "{\"Response\":\"{\\\"name\\\": \\\"Tilt BLACK\\\", \\\"gravity\\\": 1098, \\\"gravity_unit\\\": \\\"G\\\", \\\"temp\\\": 65, \\\"temp_unit\\\": \\\"F\\\"}\"}", + Response: "{\"Response\":\"{\\\"color\\\": \\\"BLACK\\\", \\\"gravity\\\": 1098, \\\"mac\\\": \\\"66:77:88:99:00\\\", \\\"temp\\\": 65, \\\"timestamp\\\": \\\"1661445284\\\", \\\"gravity_unit\\\": \\\"G\\\", \\\"temp_unit\\\": \\\"F\\\"}\"}", CallCount: 2, CallSignature: "GET http://fake.com", }, @@ -92,7 +92,7 @@ func TestWebhook(t *testing.T) { } for _, theT := range theTests { - httpmock.RegisterResponder(theT.in.Method, theT.in.Url, + httpmock.RegisterResponder(theT.in.Method, theT.in.URL, func(req *http.Request) (*http.Response, error) { buf := new(bytes.Buffer) buf.ReadFrom(req.Body) @@ -104,7 +104,7 @@ func TestWebhook(t *testing.T) { t.Run(theT.name, func(t *testing.T) { sampleConfig := tilt.ParseConfig("some/file/somewhere.toml") - sampleConfig.ConfigData.Set("webhook.url", theT.in.Url) + sampleConfig.ConfigData.Set("webhook.url", theT.in.URL) sampleConfig.ConfigData.Set("webhook.headers", theT.in.Headers) sampleConfig.ConfigData.Set("webhook.template", theT.in.Template) sampleConfig.ConfigData.Set("webhook.method", theT.in.Method) diff --git a/main.go b/main.go index 4ba3e64..be9fbc8 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "encoding/hex" "errors" "fmt" - "github.com/go-kit/kit/log/level" + "github.com/go-kit/log/level" "github.com/go-playground/validator/v10" "github.com/myoung34/gatt" "github.com/myoung34/gatt/examples/option" @@ -56,7 +56,7 @@ func OnStateChanged(device gatt.Device, s gatt.State) { } } -func NewTilt(data []byte) (tilt.TiltPayload, error) { +func NewTilt(data []byte) (tilt.Payload, error) { // http://www.havlena.net/wp-content/themes/striking/includes/timthumb.php?src=/wp-content/uploads/ibeacon-packet.png&w=600&zc=1 //pkt = b' \x04>* \x02\x01x03\x01w\t \xbc\xd0W\xef\x1e\x02\x01\x04\x1a\xffL\x00\x02\x15 \xa4\x95\xbb0\xc5\xb1KD\xb5\x12\x13p\xf0-t\xde \x00B \x03\xf7 \xc5\xa7' # noqa // | | | | | | | | | # noqa @@ -65,10 +65,10 @@ func NewTilt(data []byte) (tilt.TiltPayload, error) { // | | | mac addr | uuid | unused data | major| minor | tx | # noqa // | | | | | if len(data) < 25 || binary.BigEndian.Uint32(data) != 0x4c000215 { - return tilt.TiltPayload{}, errors.New("not an iBeacon") + return tilt.Payload{}, errors.New("not an iBeacon") } - return tilt.TiltPayload{ - Id: strings.ToLower(strings.Replace(strings.ToUpper(hex.EncodeToString(data[4:8])+"-"+hex.EncodeToString(data[8:10])+"-"+hex.EncodeToString(data[10:12])+"-"+hex.EncodeToString(data[12:14])+"-"+hex.EncodeToString(data[14:20])), "-", "", -1)), + return tilt.Payload{ + ID: strings.ToLower(strings.Replace(strings.ToUpper(hex.EncodeToString(data[4:8])+"-"+hex.EncodeToString(data[8:10])+"-"+hex.EncodeToString(data[10:12])+"-"+hex.EncodeToString(data[12:14])+"-"+hex.EncodeToString(data[14:20])), "-", "", -1)), Major: binary.BigEndian.Uint16(data[20:22]), Minor: binary.BigEndian.Uint16(data[22:24]), }, nil @@ -77,18 +77,18 @@ func NewTilt(data []byte) (tilt.TiltPayload, error) { func OnPeripheralDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) { _tilt, err := NewTilt(a.ManufacturerData) if err == nil { - payload := tilt.TiltPayload{ - Id: _tilt.Id, + payload := tilt.Payload{ + ID: _tilt.ID, Mac: p.ID(), - Color: tilt.TiltMap[_tilt.Id], + Color: tilt.TiltMap[_tilt.ID], Major: _tilt.Major, Minor: _tilt.Minor, Rssi: rssi, - Timestamp: time.Now().String(), + Timestamp: time.Now().UTC().Unix(), } err = validate.Struct(payload) if err == nil { - level.Info(tilt.Logger).Log("main.OnPeripheralDiscovered", fmt.Sprintf("%s [%s] temp: %d gravity: %d rssi: %d", payload.Id, payload.Color, payload.Major, payload.Minor, rssi)) + level.Info(tilt.Logger).Log("main.OnPeripheralDiscovered", fmt.Sprintf("%s [%s] temp: %d gravity: %d rssi: %d", payload.ID, payload.Color, payload.Major, payload.Minor, rssi)) returnStr, _ := callEmitter(fmt.Sprintf("%s.emit", config.EnabledEmitter), payload, config.ConfigData.Get(config.EnabledEmitter)) level.Info(tilt.Logger).Log("main.OnPeripheralDiscovered", returnStr) } @@ -96,7 +96,7 @@ func OnPeripheralDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) } } -func callEmitter(funcName string, payload tilt.TiltPayload, emitterConfig interface{}) (result interface{}, err error) { +func callEmitter(funcName string, payload tilt.Payload, emitterConfig interface{}) (result interface{}, err error) { level.Info(tilt.Logger).Log("main.callEmitter", fmt.Sprintf("Attempting to call %+v", funcName)) _, ok := EmittersMap[funcName] if !ok { @@ -106,8 +106,7 @@ func callEmitter(funcName string, payload tilt.TiltPayload, emitterConfig interf in := make([]reflect.Value, 2) in[0] = reflect.ValueOf(payload) in[1] = reflect.ValueOf(emitterConfig) - var res []reflect.Value - res = f.Call(in) + var res = f.Call(in) result = res[0].Interface() return } diff --git a/sider.yml b/sider.yml index fea293a..77dc443 100644 --- a/sider.yml +++ b/sider.yml @@ -5,7 +5,7 @@ linter: target: ./... config: .golangci.yml enable: - - golint + - revive - gosec fast: true no-config: false diff --git a/tilt/config.go b/tilt/config.go index 3339999..06357c0 100644 --- a/tilt/config.go +++ b/tilt/config.go @@ -3,7 +3,7 @@ package tilt import ( "errors" "fmt" - "github.com/go-kit/kit/log/level" + "github.com/go-kit/log/level" "github.com/spf13/viper" "os" "path/filepath" diff --git a/tilt/device.go b/tilt/device.go index 4c09b3a..a55d291 100644 --- a/tilt/device.go +++ b/tilt/device.go @@ -13,12 +13,12 @@ var TiltMap = map[string]string{ "25cc0b60914de76ead903f903bfd5e53": "MIGHTY", } -type TiltPayload struct { - Id string +type Payload struct { + ID string Mac string Color string `validate:"required"` Major uint16 Minor uint16 Rssi int - Timestamp string + Timestamp int64 } diff --git a/tilt/logger.go b/tilt/logger.go index 0227ef9..9b8ce9c 100644 --- a/tilt/logger.go +++ b/tilt/logger.go @@ -2,8 +2,8 @@ package tilt import ( "fmt" - "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" + "github.com/go-kit/log" + "github.com/go-kit/log/level" "os" "strings" )