diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 167716e2f9b1d2055d3537bc344959b220b7dceb..0000000000000000000000000000000000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-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
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index ea5101d1c53b4df8e71b359d5634e5d3db678653..0000000000000000000000000000000000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,12 +0,0 @@
-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"]
diff --git a/api/joinroom.go b/api_joinroom.go
similarity index 82%
rename from api/joinroom.go
rename to api_joinroom.go
index 5826d7844ae78f05eab07696df28ea2ff1cef7bb..32477d391b6d916f513668833a43399538777763 100644
--- a/api/joinroom.go
+++ b/api_joinroom.go
@@ -1,11 +1,11 @@
-package api
+package matrixbot
 
 import (
 	"fmt"
 	"net/http"
 )
 
-func JoinRoom(token Token, roomId string) error {
+func JoinRoom(token MatrixToken, roomId string) error {
 	request, err := http.NewRequest(
 		http.MethodPost,
 		fmt.Sprintf("https://matrix-client.matrix.org/_matrix/client/v3/rooms/%s/join", roomId),
diff --git a/api/login.go b/api_login.go
similarity index 98%
rename from api/login.go
rename to api_login.go
index 3cb9afff16b157878d605d2af1ad9a77b4881eb7..afc9e6f92f913f905ff9b51f4a54de22abcd739b 100644
--- a/api/login.go
+++ b/api_login.go
@@ -1,4 +1,4 @@
-package api
+package matrixbot
 
 import (
 	"bytes"
diff --git a/api/markread.go b/api_markread.go
similarity index 90%
rename from api/markread.go
rename to api_markread.go
index 7bbdaa7edf3707b4a5d0d97b0f272b040da9ba42..b14aeb02063b4d9c3d2d387c42a65a854e35dd98 100644
--- a/api/markread.go
+++ b/api_markread.go
@@ -1,4 +1,4 @@
-package api
+package matrixbot
 
 import (
 	"bytes"
@@ -15,7 +15,7 @@ type ReadReceiptRequest struct {
 	ReadPrivate string `json:"m.read.private"`
 }
 
-func SetReadReceipt(token Token, roomId string, messageId string) error {
+func SetReadReceipt(token MatrixToken, roomId string, messageId string) error {
 	body, err := json.Marshal(ReadReceiptRequest{
 		FullyRead:   messageId,
 		Read:        messageId,
diff --git a/api/refresh.go b/api_refresh.go
similarity index 98%
rename from api/refresh.go
rename to api_refresh.go
index 1dcc5b72cf6ddd4e8581eb12b0f356615c03e7f5..ebefbe522d4b2b3c21415ecaecbff61ccd25ea34 100644
--- a/api/refresh.go
+++ b/api_refresh.go
@@ -1,4 +1,4 @@
-package api
+package matrixbot
 
 import (
 	"bytes"
diff --git a/api/sendmessage.go b/api_sendmessage.go
similarity index 89%
rename from api/sendmessage.go
rename to api_sendmessage.go
index 70e480d43bd9453021140cea57419ce5c071ed74..a7faa3d8b6a9e9082ba3dd888b83f06270c003af 100644
--- a/api/sendmessage.go
+++ b/api_sendmessage.go
@@ -1,4 +1,4 @@
-package api
+package matrixbot
 
 import (
 	"bytes"
@@ -8,7 +8,7 @@ import (
 	"net/http"
 )
 
-func SendMessage(token Token, roomId string, content interface{}) error {
+func SendMessage(token MatrixToken, roomId string, content interface{}) error {
 	transactionId, err := uuid.NewRandom()
 	if err != nil {
 		return err
diff --git a/api/setpusher.go b/api_setpusher.go
similarity index 95%
rename from api/setpusher.go
rename to api_setpusher.go
index c223f86c13876bc9c05474ca0f5d1b2c7c9fa5c3..e1ed92e0f2716eed2ac4b95ea00a47f093e30dd4 100644
--- a/api/setpusher.go
+++ b/api_setpusher.go
@@ -1,4 +1,4 @@
-package api
+package matrixbot
 
 import (
 	"bytes"
@@ -26,7 +26,7 @@ type PusherData struct {
 	Url    string `json:"url"`
 }
 
-func SetPusher(token Token, url string) error {
+func SetPusher(token MatrixToken, url string) error {
 	body, err := json.Marshal(PusherRequest{
 		AppDisplayName: "webhook",
 		AppId:          "de.justjanne.webhook",
diff --git a/go.mod b/go.mod
index 44a23d67472bde27cc99846352e0d19e04af04ed..b784d8d27971e3d490476e02d20787132af8e532 100644
--- a/go.mod
+++ b/go.mod
@@ -1,15 +1,5 @@
-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
diff --git a/go.sum b/go.sum
index 0425cac525dfbe7398024a83fb691ffe627077fa..7790d7c3e03900e267f0aade3acce649895b2246 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,2 @@
-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=
diff --git a/main.go b/main.go
deleted file mode 100644
index 218ce4d7c9ac17247f694b73a4ce9e89fac34921..0000000000000000000000000000000000000000
--- a/main.go
+++ /dev/null
@@ -1,126 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"fmt"
-	"git.kuschku.de/justJanne/bahn-api"
-	"git.kuschku.de/justjanne/stateless-matrix-bot/api"
-	"html/template"
-	"math/rand/v2"
-	"net/http"
-	"net/url"
-	"os"
-	"strings"
-)
-
-func main() {
-	var err error
-
-	pushUrl, err := url.Parse(os.Getenv("BOT_PUSHURL"))
-	if err != nil {
-		panic(err)
-	}
-	bot := NewMatrixBot(pushUrl)
-
-	// !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
-		}
-		answer := answers[rand.IntN(len(answers))]
-
-		err = api.SendMessage(*bot.token, notification.RoomId, api.MessageContent{
-			Body:    answer,
-			MsgType: "m.text",
-		})
-		return nil
-	})
-
-	// !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]))
-		if err != nil {
-			return err
-		}
-		timetable, err := bahn.TimetableFromReader(response.Body)
-		if err != nil {
-			return err
-		}
-		buf := new(bytes.Buffer)
-		err = bahnTpl.Execute(buf, timetable)
-		if err != nil {
-			return err
-		}
-		err = api.SendMessage(*bot.token, notification.RoomId, api.MessageContent{
-			FormattedBody: buf.String(),
-			Format:        "org.matrix.custom.html",
-			MsgType:       "m.text",
-		})
-		return nil
-	})
-	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")
-}
diff --git a/matrixbot.go b/matrixbot.go
index 2604f8fcd5c59bee008f4d1be4a2e24595531342..3c6237c34978a92f49d6d8f33371d4273b4834e4 100644
--- a/matrixbot.go
+++ b/matrixbot.go
@@ -1,8 +1,7 @@
-package main
+package matrixbot
 
 import (
 	"fmt"
-	"git.kuschku.de/justjanne/stateless-matrix-bot/api"
 	"io"
 	"log"
 	"net/http"
@@ -12,16 +11,16 @@ import (
 )
 
 type MatrixBot struct {
-	token    *api.Token
+	token    *MatrixToken
 	pushUrl  *url.URL
-	handlers map[string]func(bot *MatrixBot, notification api.Notification) error
+	handlers map[string]func(bot *MatrixBot, notification 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),
+		handlers: make(map[string]func(bot *MatrixBot, notification Notification) error),
 	}
 }
 
@@ -29,11 +28,11 @@ func (bot *MatrixBot) RefreshToken() error {
 	if bot.token == nil {
 		return fmt.Errorf("no refresh token available")
 	}
-	userData, err := api.Refresh(bot.token.RefreshToken)
+	userData, err := Refresh(bot.token.RefreshToken)
 	if err != nil {
 		return err
 	}
-	bot.token = &api.Token{
+	bot.token = &MatrixToken{
 		AccessToken:  userData.AccessToken,
 		Expires:      time.Now().Add(time.Duration(userData.ExpiresInMs) / 2 * time.Millisecond),
 		RefreshToken: userData.RefreshToken,
@@ -42,11 +41,11 @@ func (bot *MatrixBot) RefreshToken() error {
 }
 
 func (bot *MatrixBot) Login(username string, password string, deviceId string) error {
-	userData, err := api.Login(username, password, deviceId)
+	userData, err := Login(username, password, deviceId)
 	if err != nil {
 		return err
 	}
-	bot.token = &api.Token{
+	bot.token = &MatrixToken{
 		AccessToken:  userData.AccessToken,
 		Expires:      time.Now().Add(time.Duration(userData.ExpiresInMs) / 2 * time.Millisecond),
 		RefreshToken: userData.RefreshToken,
@@ -66,10 +65,10 @@ func (bot *MatrixBot) RefreshTask() {
 }
 
 func (bot *MatrixBot) RegisterPusher() error {
-	return api.SetPusher(*bot.token, bot.pushUrl.String())
+	return SetPusher(*bot.token, bot.pushUrl.String())
 }
 
-func (bot *MatrixBot) HandleFunc(command string, handler func(bot *MatrixBot, notification api.Notification) error) {
+func (bot *MatrixBot) HandleFunc(command string, handler func(bot *MatrixBot, notification Notification) error) {
 	bot.handlers[command] = handler
 }
 
@@ -80,7 +79,7 @@ func (bot *MatrixBot) Serve(endpoint string) {
 	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)
+		notification, err := ParseNotification(request.Body)
 		if err != nil {
 			log.Println(err.Error())
 			return
@@ -88,7 +87,7 @@ func (bot *MatrixBot) Serve(endpoint string) {
 		if notification.EventId == "" {
 			return
 		}
-		err = api.SetReadReceipt(*bot.token, notification.RoomId, notification.EventId)
+		err = SetReadReceipt(*bot.token, notification.RoomId, notification.EventId)
 		if err != nil {
 			log.Println(err.Error())
 			return
diff --git a/api/token.go b/model_matrixtoken.go
similarity index 65%
rename from api/token.go
rename to model_matrixtoken.go
index fa9c55954fdde2f1da8ce67522ebd5ef519d5d4b..7a8221adca1ee390c39c77b1605431abe148c034 100644
--- a/api/token.go
+++ b/model_matrixtoken.go
@@ -1,8 +1,8 @@
-package api
+package matrixbot
 
 import "time"
 
-type Token struct {
+type MatrixToken struct {
 	AccessToken  string
 	Expires      time.Time
 	RefreshToken string
diff --git a/api/notification.go b/model_notification.go
similarity index 98%
rename from api/notification.go
rename to model_notification.go
index 8f7dd6537cdf5ea7373300cf412eafda4303f4f6..933a424f5564e1bec004793e1b4881e67be4b765 100644
--- a/api/notification.go
+++ b/model_notification.go
@@ -1,4 +1,4 @@
-package api
+package matrixbot
 
 import (
 	"encoding/json"