Skip to content

Commit

Permalink
Initial commit 🎉
Browse files Browse the repository at this point in the history
  • Loading branch information
puria committed Sep 19, 2024
0 parents commit c94fea5
Show file tree
Hide file tree
Showing 11 changed files with 364 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .cruft.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"template": "https://github.com/dyne/starters",
"commit": "4f756cc0a66c09aebf61786e5db8251290b19252",
"checkout": null,
"context": {
"cookiecutter": {
"project_name": "mattermost-commands",
"project_slug": "mattermost_commands",
"project_description": "Custom commands for chat.dyne.org",
"full_name": "Puria Nafisi Azizi",
"email": "puria@dyne.org",
"repo": "github.com/dyne/mattermost_commands",
"go_version": "1.22",
"_template": "https://github.com/dyne/starters"
}
},
"directory": "go"
}
25 changes: 25 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Top-most EditorConfig file
root = true

# Every file should according to these default configurations if not specified
[*]
# Use UNIX-style line endings
end_of_line = LF
# Use utf-8 file encoding
charset = utf-8
# 4 space indent
indent_style = space
indent_size = 4
# Ensure file ends with a newline when saving(prevent `no newline at EOF`)
insert_final_newline = true
# Remove any whitespace characters preceding newline characters
trim_trailing_whitespace = true

# For YAML
[*.{yml,yaml}]
indent_size = 2

# For Go files
[*.go]
# `gofmt` uses tabs for indentation
indent_style = tab
21 changes: 21 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
on:
release:
types: [created]
push:
branches: [main]

permissions:
contents: write
packages: write

jobs:
release-linux-amd64:
name: release linux/amd64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wangyoucao577/go-release-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: linux
goarch: amd64
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Allowlisting gitignore template for GO projects prevents us
# from adding various unwanted local files, such as generated
# files, developer configurations or IDE-specific files etc.

# Ignore everything
*

# But not these files...
!/.gitignore
!/.editorconfig
!/.vroom/*
!/.golangci.yml
!/.env.sample
!/.cruft.json

!.github/workflows/*.yml

!*.go
!go.sum
!go.mod

!README.md
!LICENSE

!Makefile

# ...even if they are in subdirectories
!*/
29 changes: 29 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
run:
tests: true

issues:
include:
- EXC0001
- EXC0005
- EXC0011
- EXC0012
- EXC0013

max-issues-per-linter: 0
max-same-issues: 0

linters:
enable:
- bodyclose
- exportloopref
- goimports
- gosec
- nilerr
- predeclared
- revive
- rowserrcheck
- sqlclosecheck
- tparallel
- unconvert
- unparam
- whitespace
101 changes: 101 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Based on a true story on:
# https://gist.github.com/thomaspoignant/5b72d579bd5f311904d973652180c705

GOCMD=go
GOTEST=$(GOCMD) test
GOVET=$(GOCMD) vet
BINARY_NAME=mattermost_commands
VERSION?=0.0.0
SERVICE_PORT?=3000
DOCKER_REGISTRY?= #if set it should finished by /
EXPORT_RESULT?=false # for CI please set EXPORT_RESULT to true

GREEN := $(shell tput -Txterm setaf 2)
YELLOW := $(shell tput -Txterm setaf 3)
WHITE := $(shell tput -Txterm setaf 7)
CYAN := $(shell tput -Txterm setaf 6)
RESET := $(shell tput -Txterm sgr0)

.PHONY: all test build vendor

all: help

## Build:
build: vendor ## Build your project and put the output binary in out/bin/
mkdir -p out/bin
GO111MODULE=on $(GOCMD) build -mod vendor -o out/bin/$(BINARY_NAME) .

clean: ## Remove build related file
rm -fr ./bin
rm -fr ./out
rm -f ./junit-report.xml checkstyle-report.xml ./coverage.xml ./profile.cov yamllint-checkstyle.xml

vendor: ## Copy of all packages needed to support builds and tests in the vendor directory
$(GOCMD) mod vendor

watch: ## Run the code with cosmtrek/air to have automatic reload on changes
$(eval PACKAGE_NAME=$(shell head -n 1 go.mod | cut -d ' ' -f2))
docker run -it --rm -w /go/src/$(PACKAGE_NAME) -v $(shell pwd):/go/src/$(PACKAGE_NAME) -p $(SERVICE_PORT):$(SERVICE_PORT) cosmtrek/air

## Test:
test: ## Run the tests of the project
ifeq ($(EXPORT_RESULT), true)
GO111MODULE=off go get -u github.com/jstemmer/go-junit-report
$(eval OUTPUT_OPTIONS = | tee /dev/tty | go-junit-report -set-exit-code > junit-report.xml)
endif
$(GOTEST) -v -race ./... $(OUTPUT_OPTIONS)

coverage: ## Run the tests of the project and export the coverage
$(GOTEST) -cover -covermode=count -coverprofile=profile.cov ./...
$(GOCMD) tool cover -func profile.cov
ifeq ($(EXPORT_RESULT), true)
GO111MODULE=off go get -u github.com/AlekSi/gocov-xml
GO111MODULE=off go get -u github.com/axw/gocov/gocov
gocov convert profile.cov | gocov-xml > coverage.xml
endif

## Lint:
lint: lint-go lint-dockerfile lint-yaml ## Run all available linters

lint-dockerfile: ## Lint your Dockerfile
# If dockerfile is present we lint it.
ifeq ($(shell test -e ./Dockerfile && echo -n yes),yes)
$(eval CONFIG_OPTION = $(shell [ -e $(shell pwd)/.hadolint.yaml ] && echo "-v $(shell pwd)/.hadolint.yaml:/root/.config/hadolint.yaml" || echo "" ))
$(eval OUTPUT_OPTIONS = $(shell [ "${EXPORT_RESULT}" == "true" ] && echo "--format checkstyle" || echo "" ))
$(eval OUTPUT_FILE = $(shell [ "${EXPORT_RESULT}" == "true" ] && echo "| tee /dev/tty > checkstyle-report.xml" || echo "" ))
docker run --rm -i $(CONFIG_OPTION) hadolint/hadolint hadolint $(OUTPUT_OPTIONS) - < ./Dockerfile $(OUTPUT_FILE)
endif

lint-go: ## Use golintci-lint on your project
$(eval OUTPUT_OPTIONS = $(shell [ "${EXPORT_RESULT}" == "true" ] && echo "--out-format checkstyle ./... | tee /dev/tty > checkstyle-report.xml" || echo "" ))
docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:latest-alpine golangci-lint run --deadline=65s $(OUTPUT_OPTIONS)

lint-yaml: ## Use yamllint on the yaml file of your projects
ifeq ($(EXPORT_RESULT), true)
GO111MODULE=off go get -u github.com/thomaspoignant/yamllint-checkstyle
$(eval OUTPUT_OPTIONS = | tee /dev/tty | yamllint-checkstyle > yamllint-checkstyle.xml)
endif
docker run --rm -it -v $(shell pwd):/data cytopia/yamllint -f parsable $(shell git ls-files '*.yml' '*.yaml') $(OUTPUT_OPTIONS)

## Docker:
docker-build: ## Use the dockerfile to build the container
docker build --rm --tag $(BINARY_NAME) .

docker-release: ## Release the container with tag latest and version
docker tag $(BINARY_NAME) $(DOCKER_REGISTRY)$(BINARY_NAME):latest
docker tag $(BINARY_NAME) $(DOCKER_REGISTRY)$(BINARY_NAME):$(VERSION)
# Push the docker images
docker push $(DOCKER_REGISTRY)$(BINARY_NAME):latest
docker push $(DOCKER_REGISTRY)$(BINARY_NAME):$(VERSION)

## Help:
help: ## Show this help.
@echo ''
@echo 'Usage:'
@echo ' ${YELLOW}make${RESET} ${GREEN}<target>${RESET}'
@echo ''
@echo 'Targets:'
@awk 'BEGIN {FS = ":.*?## "} { \
if (/^[a-zA-Z_-]+:.*?##.*$$/) {printf " ${YELLOW}%-20s${GREEN}%s${RESET}\n", $$1, $$2} \
else if (/^## .*$$/) {printf " ${CYAN}%s${RESET}\n", substr($$1,4)} \
}' $(MAKEFILE_LIST)
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


## 📝 Site docs

```bash
go install golang.org/x/pkgsite/cmd/pkgsite@latest && pkgsite
```
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/dyne/mattermost_commands

go 1.22
11 changes: 11 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package main

import (
"log"
"net/http"
)

func main() {
handler := http.HandlerFunc(MiniServer)
log.Fatal(http.ListenAndServe(":4444", handler))
}
49 changes: 49 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)

// The mattermost thing
type Notification struct {
Text string `json:"text"`
ResponseType string `json:"response_type"`
GotoLocation string `json:"goto_location"`
}

// The serverino
func MiniServer(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
panic(err)
}
address := getEncodedAddress(r.FormValue("text"))
text := fmt.Sprintf(`🚨 **Hey beautiful souls!** 🚨
If you're free, we’ve got a videoconference happening today, and I’d love for you to join! 🎥💻
🗓️ **When?** Today
⏰ **Time?** Right now (or whenever you're ready, coffee ☕️ in hand)
💻 **Where?** %s
It'll be great to have you there—let's connect and make it a good one! 💫💬
– *[%s]* 😊✨
`, address, r.FormValue("user_name"))
data := Notification{text, "in_channel", address}
w.Header().Set("Content-type", "application/json")
json.NewEncoder(w).Encode(&data)
}

func getEncodedAddress(path string) string {
baseURL, err := url.Parse("https://vdc.dyne.org")
if err != nil {
fmt.Println("Malformed URL: ", err.Error())
}

baseURL.Path += path
return baseURL.String()
}
72 changes: 72 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)

func TestGETVDC(t *testing.T) {
cases := []struct {
got string
want string
}{
{"", "https://vdc.dyne.org"},
{"12", "https://vdc.dyne.org/12"},
{"mimmo", "https://vdc.dyne.org/mimmo"},
{"a/b", "https://vdc.dyne.org/a/b"},
{"a?b=1", "https://vdc.dyne.org/a%3Fb=1"},
{"⛅", "https://vdc.dyne.org/%E2%9B%85"},
{"me space", "https://vdc.dyne.org/me%20space"},
}
for _, test := range cases {
t.Run(fmt.Sprintf("return a correct link for %q is %q", test.got, test.want), func(t *testing.T) {
form := url.Values{}
form.Add("channel_id", "fukxanjgjbnp7ng383at53k1sy")
form.Add("channel_name", "town-square")
form.Add("command", "/weather")
form.Add("response_url", "http://localhost:8066/hooks/commands/i11f6nnfgfyk8eg56x9omc6dpa")
form.Add("team_domain", "team-awesome")
form.Add("team_id", "wx4zz8t4ttgmtxqiwfohijayzc")
form.Add("text", test.got)
form.Add("token", "qzgakf1nx3yt9dr4n8585ihbxy")
form.Add("trigger_id", "ZWZ5ZjRndzR4YmJxOHJlZWh4MXpkaHozbnI6ZXJqNnFjazNyZmd0dWpzODZ3NXI2cmNremg6MTY2MjA0MTY5Njg5NjpNRVFDSUQ5cTZ3MkRHU1RaNjhyaDh1TGl1STlSVHh2R1czSXZ5aGVRYjhkWThuZnlBaUI2YnlPR2ZpWlczR1FmVkdIODlreEp4MmlVT0UxMm9LMjlkZ1d0RC8xbjZRPT0=")
form.Add("user_id", "erj6qck3rfgtujs86w5r6rckzh")
form.Add("user_name", "alan")

request, _ := http.NewRequest(http.MethodPost, test.got, bytes.NewBufferString(form.Encode()))
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
request.Header.Set("Accept", "application/json")
request.Header.Set("Accept-Encoding", "gzip")
request.Header.Set("Authorization", "Token qzgakf1nx3yt9dr4n8585ihbxy")
request.Header.Set("User-Agent", "Mattermost-Bot/1.1")
response := httptest.NewRecorder()

MiniServer(response, request)
var notification Notification
err := json.NewDecoder(response.Body).Decode(&notification)

if err != nil {
t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err)
}

got := notification.GotoLocation

if got != test.want {
t.Errorf("got %q, want %q", got, test.want)
}
assertStatus(t, response.Code, http.StatusOK)
})
}
}

func assertStatus(t testing.TB, got, want int) {
t.Helper()
if got != want {
t.Errorf("did not get correct status, got %d, want %d", got, want)
}
}

0 comments on commit c94fea5

Please sign in to comment.