Skip to content
Snippets Groups Projects
Verified Commit 595e7c6f authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

feat: split framework from prototype

parent cb985aef
No related branches found
No related tags found
No related merge requests found
container:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [ "" ]
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA} --destination $CI_REGISTRY_IMAGE:latest
FROM golang:1.24-alpine AS go_builder
WORKDIR /src
COPY go.* ./
RUN go mod download
COPY . ./
RUN CGO_ENABLED=0 GOOS=linux go build -o app
FROM scratch
COPY --from=go_builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=go_builder /src/app /app
ENTRYPOINT ["/app"]
module git.kuschku.de/justjanne/stateless-matrix-bot
module git.kuschku.de/justjanne/stateless-matrix-bot-framework
go 1.22.2
require (
git.kuschku.de/justJanne/bahn-api v0.0.0-20210606022125-173e9216d8a8
github.com/google/uuid v1.6.0
)
require (
github.com/andybalholm/cascadia v1.0.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01 // indirect
golang.org/x/text v0.3.2 // indirect
)
require github.com/google/uuid v1.6.0
git.kuschku.de/justJanne/bahn-api v0.0.0-20210606022125-173e9216d8a8 h1:5VmfteMrWeABylH1lP46QLBEx8YWawIfw2WdfGWSv/I=
git.kuschku.de/justJanne/bahn-api v0.0.0-20210606022125-173e9216d8a8/go.mod h1:9d+hDIsjtAxjb0FPo6DLNqf9Co7CX35IHScmo9wQGlo=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01 h1:po1f06KS05FvIQQA2pMuOWZAUXiy1KYdIf0ElUU2Hhc=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
package main
import (
"bytes"
"fmt"
"git.kuschku.de/justJanne/bahn-api"
"git.kuschku.de/justjanne/stateless-matrix-bot/api"
"html/template"
"math/rand/v2"
"git.kuschku.de/justjanne/stateless-matrix-bot-framework/api"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"time"
)
func main() {
var err error
type MatrixBot struct {
token *api.Token
pushUrl *url.URL
handlers map[string]func(bot *MatrixBot, notification api.Notification) error
}
func NewMatrixBot(pushUrl *url.URL) *MatrixBot {
return &MatrixBot{
token: nil,
pushUrl: pushUrl,
handlers: make(map[string]func(bot *MatrixBot, notification api.Notification) error),
}
}
pushUrl, err := url.Parse(os.Getenv("BOT_PUSHURL"))
func (bot *MatrixBot) RefreshToken() error {
if bot.token == nil {
return fmt.Errorf("no refresh token available")
}
userData, err := api.Refresh(bot.token.RefreshToken)
if err != nil {
panic(err)
return err
}
bot.token = &api.Token{
AccessToken: userData.AccessToken,
Expires: time.Now().Add(time.Duration(userData.ExpiresInMs) / 2 * time.Millisecond),
RefreshToken: userData.RefreshToken,
}
bot := NewMatrixBot(pushUrl)
return nil
}
// !8ball handler
bot.HandleFunc("!8ball", func(bot *MatrixBot, notification api.Notification) error {
positive := []string{
"It is certain",
"It is decidedly so",
"Without a doubt",
"Yes – definitely",
"You may rely on it",
"As I see it, yes",
"Most Likely",
"Outlook good",
"Yes",
"Signs point to yes.",
}
negative := []string{
"Don’t count on it",
"My reply is no",
"My sources say no",
"Outlook not so good",
"very doubtful",
}
neutral := []string{
"Reply hazy",
"try again",
"Ask again later",
"Better not tell you now",
"Cannot predict now",
"Concentrate and ask again",
}
var answers []string
switch rand.IntN(3) {
case 0:
answers = positive
break
case 1:
answers = negative
break
case 2:
answers = neutral
break
func (bot *MatrixBot) Login(username string, password string, deviceId string) error {
userData, err := api.Login(username, password, deviceId)
if err != nil {
return err
}
bot.token = &api.Token{
AccessToken: userData.AccessToken,
Expires: time.Now().Add(time.Duration(userData.ExpiresInMs) / 2 * time.Millisecond),
RefreshToken: userData.RefreshToken,
}
return nil
}
func (bot *MatrixBot) RefreshTask() {
for true {
if bot.token != nil && time.Now().After(bot.token.Expires) {
if err := bot.RefreshToken(); err != nil {
log.Printf("error refresh token: %s\n", err.Error())
}
}
answer := answers[rand.IntN(len(answers))]
time.Sleep(1 * time.Second)
}
}
err = api.SendMessage(*bot.token, notification.RoomId, api.MessageContent{
Body: answer,
MsgType: "m.text",
})
return nil
})
func (bot *MatrixBot) RegisterPusher() error {
return api.SetPusher(*bot.token, bot.pushUrl.String())
}
// !trains handler
bahnTpl, err := template.New("bahn").Parse(`{{- /*gotype: bahn.Timetable*/ -}}
<b>{{.Station}}</b> Abfahrten
{{- range .Stops -}}
{{- if .Departure -}}
{{- if .Departure.Line -}}
<li>{{- if .Departure.ChangedTime -}}{{.Departure.ChangedTime}}{{- else if .Departure.PlannedTime -}}{{.Departure.PlannedTime}}{{- end -}}
· <b>{{ .Departure.Line }}</b></li>
{{- else if .TripLabel.TripCategory -}}
{{- if .TripLabel.TripNumber -}}
<li>{{- if .Departure.ChangedTime -}}{{.Departure.ChangedTime}}{{- else if .Departure.PlannedTime -}}{{.Departure.PlannedTime}}{{- end -}}
· <b>{{ .TripLabel.TripCategory }} {{ .TripLabel.TripNumber }}</b></li>
{{- end -}}
{{- end -}}
{{- end -}}
{{- end -}}`)
if err != nil {
panic(err)
}
bot.HandleFunc("!trains", func(bot *MatrixBot, notification api.Notification) error {
elements := strings.SplitN(strings.TrimSpace(notification.Content.Body), " ", 3)
response, err := http.Get(fmt.Sprintf("https://iris.noncd.db.de/iris-tts/timetable/fchg/%s", elements[1]))
func (bot *MatrixBot) HandleFunc(command string, handler func(bot *MatrixBot, notification api.Notification) error) {
bot.handlers[command] = handler
}
func (bot *MatrixBot) Serve(endpoint string) {
http.HandleFunc("/healthz", func(writer http.ResponseWriter, request *http.Request) {
_, _ = io.WriteString(writer, "OK\n")
})
log.Printf("listening for push notifications on %s\n", bot.pushUrl.Path)
http.HandleFunc(bot.pushUrl.Path, func(writer http.ResponseWriter, request *http.Request) {
log.Println("Received push notification")
notification, err := api.ParseNotification(request.Body)
if err != nil {
return err
log.Println(err.Error())
return
}
if notification.EventId == "" {
return
}
timetable, err := bahn.TimetableFromReader(response.Body)
err = api.SetReadReceipt(*bot.token, notification.RoomId, notification.EventId)
if err != nil {
return err
log.Println(err.Error())
return
}
buf := new(bytes.Buffer)
err = bahnTpl.Execute(buf, timetable)
command := strings.SplitN(notification.Content.Body, " ", 2)[0]
handler, ok := bot.handlers[command]
if !ok {
log.Printf("could not find handler for '%s'\n", command)
return
}
err = handler(bot, notification)
if err != nil {
return err
log.Println(err.Error())
return
}
err = api.SendMessage(*bot.token, notification.RoomId, api.MessageContent{
FormattedBody: buf.String(),
Format: "org.matrix.custom.html",
MsgType: "m.text",
})
return nil
writer.WriteHeader(204)
})
err = bot.Login(os.Getenv("BOT_USERNAME"), os.Getenv("BOT_PASSWORD"), os.Getenv("BOT_DEVICEID"))
if err != nil {
panic(err)
}
err = bot.RegisterPusher()
if err != nil {
panic(err)
}
go bot.RefreshTask()
bot.Serve(":8080")
fmt.Printf("listening for requests on %s\n", endpoint)
log.Fatal(http.ListenAndServe(endpoint, nil))
}
package main
import (
"fmt"
"git.kuschku.de/justjanne/stateless-matrix-bot/api"
"io"
"log"
"net/http"
"net/url"
"strings"
"time"
)
type MatrixBot struct {
token *api.Token
pushUrl *url.URL
handlers map[string]func(bot *MatrixBot, notification api.Notification) error
}
func NewMatrixBot(pushUrl *url.URL) *MatrixBot {
return &MatrixBot{
token: nil,
pushUrl: pushUrl,
handlers: make(map[string]func(bot *MatrixBot, notification api.Notification) error),
}
}
func (bot *MatrixBot) RefreshToken() error {
if bot.token == nil {
return fmt.Errorf("no refresh token available")
}
userData, err := api.Refresh(bot.token.RefreshToken)
if err != nil {
return err
}
bot.token = &api.Token{
AccessToken: userData.AccessToken,
Expires: time.Now().Add(time.Duration(userData.ExpiresInMs) / 2 * time.Millisecond),
RefreshToken: userData.RefreshToken,
}
return nil
}
func (bot *MatrixBot) Login(username string, password string, deviceId string) error {
userData, err := api.Login(username, password, deviceId)
if err != nil {
return err
}
bot.token = &api.Token{
AccessToken: userData.AccessToken,
Expires: time.Now().Add(time.Duration(userData.ExpiresInMs) / 2 * time.Millisecond),
RefreshToken: userData.RefreshToken,
}
return nil
}
func (bot *MatrixBot) RefreshTask() {
for true {
if bot.token != nil && time.Now().After(bot.token.Expires) {
if err := bot.RefreshToken(); err != nil {
log.Printf("error refresh token: %s\n", err.Error())
}
}
time.Sleep(1 * time.Second)
}
}
func (bot *MatrixBot) RegisterPusher() error {
return api.SetPusher(*bot.token, bot.pushUrl.String())
}
func (bot *MatrixBot) HandleFunc(command string, handler func(bot *MatrixBot, notification api.Notification) error) {
bot.handlers[command] = handler
}
func (bot *MatrixBot) Serve(endpoint string) {
http.HandleFunc("/healthz", func(writer http.ResponseWriter, request *http.Request) {
_, _ = io.WriteString(writer, "OK\n")
})
log.Printf("listening for push notifications on %s\n", bot.pushUrl.Path)
http.HandleFunc(bot.pushUrl.Path, func(writer http.ResponseWriter, request *http.Request) {
log.Println("Received push notification")
notification, err := api.ParseNotification(request.Body)
if err != nil {
log.Println(err.Error())
return
}
if notification.EventId == "" {
return
}
err = api.SetReadReceipt(*bot.token, notification.RoomId, notification.EventId)
if err != nil {
log.Println(err.Error())
return
}
command := strings.SplitN(notification.Content.Body, " ", 2)[0]
handler, ok := bot.handlers[command]
if !ok {
log.Printf("could not find handler for '%s'\n", command)
return
}
err = handler(bot, notification)
if err != nil {
log.Println(err.Error())
return
}
writer.WriteHeader(204)
})
fmt.Printf("listening for requests on %s\n", endpoint)
log.Fatal(http.ListenAndServe(endpoint, nil))
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment