diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5abf192
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# PVV Calendar -> Matrix bot
+
+This bot scrapes https://pvv.ntnu.no/hendelser/, fetches todays events and sends announcement messages in matrix.
+
+Packaged as a nix flake, see ./module.nix for options.
+
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..76728a7
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,26 @@
+{
+  "nodes": {
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1692986144,
+        "narHash": "sha256-M4VFpy7Av9j+33HF5nIGm0k2+DXXW4qSSKdidIKg5jY=",
+        "owner": "NixOS",
+        "repo": "nixpkgs",
+        "rev": "74e5bdc5478ebbe7ba5849f0d765f92757bb9dbf",
+        "type": "github"
+      },
+      "original": {
+        "id": "nixpkgs",
+        "ref": "nixos-23.05",
+        "type": "indirect"
+      }
+    },
+    "root": {
+      "inputs": {
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..dce6b47
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,57 @@
+{
+  inputs = {
+    nixpkgs.url = "nixpkgs/nixos-23.05";
+  };
+
+  outputs = { self, nixpkgs }: let
+    system = "x86_64-linux";
+    pkgs = nixpkgs.legacyPackages.${system};
+  in {
+    packages.${system} = {
+      default = self.packages.${system}.pvv-calendar-bot;
+      pvv-calendar-bot = pkgs.python3Packages.buildPythonPackage {
+        name = "pvv-calendar-bot";
+        src = ./.;
+        format = "pyproject";
+
+        nativeBuildInputs = [
+          pkgs.python3Packages.setuptools
+        ];
+
+        propagatedBuildInputs = with pkgs.python3Packages; [
+          beautifulsoup4
+          markdown2
+          matrix-nio
+          requests
+        ];
+      };
+    };
+
+    nixosModules.default = ./module.nix;
+
+    overlays.${system}.default = prevPackages: finalPackages: {
+      inherit (self.packages.${system}) pvv-calendar-bot;
+    };
+
+    nixosConfigurations."test" = nixpkgs.lib.nixosSystem {
+      system = "x86_64-linux";
+      modules = [
+        self.nixosModules.default
+        { nixpkgs.overlays = [ self.overlays."x86_64-linux".default ]; }
+        {
+          boot.isContainer = true;
+
+          services.pvv-calendar-bot = {
+            enable = true;
+            settings.matrix = {
+              channel = "testchannel";
+              user = "testuser";
+              homeserver = "pvv.ntnu.no";
+            };
+            settings.secretsFile = pkgs.writeText "calendarSecrets" "snakeoil";
+          };
+        }
+      ];
+    };
+  };
+}
diff --git a/module.nix b/module.nix
new file mode 100644
index 0000000..22989b8
--- /dev/null
+++ b/module.nix
@@ -0,0 +1,75 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.services.pvv-calendar-bot;
+  inherit (lib) mkDefault mkEnableOption mkPackageOption mkIf mkOption types mdDoc;
+in {
+
+  options.services.pvv-calendar-bot = {
+    enable = mkEnableOption (lib.mdDoc "Enable pvv-calendar-bot to post to matrix");
+
+    package = mkPackageOption pkgs "pvv-calendar-bot" {};
+    settings = {
+      onCalendar = mkOption {
+        type = types.str;
+        default = "9 0 * * *";
+        description = mdDoc "OnCalendar string for the systemd service(e.g. crontab format)";
+      };
+
+      matrix = {
+        user = mkOption {
+          type = types.str;
+          description = mdDoc "Matrix username to authenticate with";
+          example = "@bot_calendar:pvv.ntnu.no";
+        };
+        channel = mkOption {
+          type = types.str;
+          description = mdDoc "Room ID of the channel to post announcements in";
+          example = "!abcdef:matrix.org";
+        };
+        homeserver = mkOption {
+          type = types.str;
+          description = mdDoc "Matrix homeserver URL to connect to";
+          example = "https://matrix.org";
+        };
+      };
+
+      secretsFile = mkOption {
+        type = types.path;
+        description = mdDoc "Path to secrets file that defines MATRIX_ACCESS_TOKEN";
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.timers."pvv-calendar-bot" = {
+      wantedBy = [ "timers.target" ];
+      timerConfig = {
+        OnCalendar = cfg.settings.onCalendar;
+        Unit = "pvv-calendar-bot";
+      };
+    };
+
+    systemd.services."pvv-calendar-bot" = {
+      preStart = let
+        envFile = pkgs.writeText "pvv-calendar-bot-env" ''
+          MATRIX_URL=${cfg.settings.matrix.homeserver}
+          MATRIX_USER=${cfg.settings.matrix.user}
+          ANNOUNCEMENT_CHANNEL=${cfg.settings.matrix.channel}
+          MATRIX_TOKEN=@MATRIX_ACCESS_TOKEN@
+       '';
+      in ''
+        ${pkgs.replace-secret}/bin/replace-secret '@MATRIX_ACCESS_TOKEN@' ${cfg.settings.secretsFile} /run/pvv-calendar-bot/env
+      '';
+
+      serviceConfig = {
+        ExecStart = "${cfg.package}/bin/pvv-calendar-bot";
+        RuntimeDirectory = "pvv-calendar-bot";
+        DynamicUser = true;
+        EnvironmentFile = [ "-/run/pvv-calendar-bot/env" ];
+      };
+    };
+
+  };
+
+}
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..63b0dea
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,28 @@
+[build-system]
+requires = ["setuptools", "setuptools-scm"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "pvv-calendar-bot"
+authors = [
+    {name = "Felix Albrigtsen", email = "felix@albrigtsen.it"},
+    {name = "Fredrik Robertsen", email = "fredrikrobertsen7@gmail.com"}
+]
+version = "1.0.0"
+description = "PVV Calendar -> Matrix bot"
+requires-python = ">=3.10"
+keywords = []
+license = {text = "MIT"}
+dependencies = [
+    "beautifulsoup4",
+    "markdown2",
+    "matrix-nio",
+    "requests"
+]
+
+[tool.setuptools.packages.find]
+where = ["src"]
+include = ["*"]
+
+[project.scripts]
+pvv-calendar-bot = "pvv_calendar_bot:main"
diff --git a/src/pvv_calendar_bot/__init__.py b/src/pvv_calendar_bot/__init__.py
new file mode 100644
index 0000000..64a714f
--- /dev/null
+++ b/src/pvv_calendar_bot/__init__.py
@@ -0,0 +1 @@
+from pvv_calendar_bot.main import main
diff --git a/event.py b/src/pvv_calendar_bot/event.py
similarity index 100%
rename from event.py
rename to src/pvv_calendar_bot/event.py
diff --git a/sendMatrix.py b/src/pvv_calendar_bot/main.py
similarity index 76%
rename from sendMatrix.py
rename to src/pvv_calendar_bot/main.py
index 0d2082f..28289c9 100644
--- a/sendMatrix.py
+++ b/src/pvv_calendar_bot/main.py
@@ -1,4 +1,7 @@
-from event import Event
+#! /usr/bin/env python
+
+from .event import Event
+from .scraping import get_soup, process_soup, get_events_today
 
 import os
 from nio import AsyncClient
@@ -7,8 +10,6 @@ from markdown2 import Markdown
 import asyncio
 import datetime
 
-from scraping import get_soup, process_soup, get_events_today
-
 MATRIX_URL=os.environ["MATRIX_URL"]
 MATRIX_USER=os.environ["MATRIX_USER"]
 MATRIX_TOKEN=os.environ["MATRIX_TOKEN"]
@@ -44,18 +45,21 @@ async def sendMatrixAnnouncement(event: Event, channel: str = ANNOUNCEMENT_CHANN
         }
     )
 
-
-async def main() -> None:
+async def sendCalendarEvents() -> None:
     global client
     client = AsyncClient(MATRIX_URL, MATRIX_USER)
     client.access_token = MATRIX_TOKEN
 
-    s = get_soup()
-    es = get_events_today(process_soup(s))
+    scrapeData = get_soup()
+    eventsToday = get_events_today(process_soup(scrapeData))
 
-    for e in es:
-        await sendMatrixAnnouncement(e, ANNOUNCEMENT_CHANNEL, False)
+    for event in eventsToday:
+        await sendMatrixAnnouncement(event, ANNOUNCEMENT_CHANNEL, False)
 
     await client.close()
 
-asyncio.run(main())
+def main():
+    asyncio.run(sendCalendarEvents())
+
+if __name__ == "__main__":
+    main()
diff --git a/scraping.py b/src/pvv_calendar_bot/scraping.py
similarity index 98%
rename from scraping.py
rename to src/pvv_calendar_bot/scraping.py
index 1e2ecba..771c722 100644
--- a/scraping.py
+++ b/src/pvv_calendar_bot/scraping.py
@@ -1,11 +1,11 @@
+from .event import Event
+
 from typing import List
 from bs4 import BeautifulSoup
 import requests
 from operator import add
 from functools import reduce
 import datetime
-from event import Event
-
 
 def get_soup() -> BeautifulSoup:
     r = requests.get("http://www.pvv.ntnu.no/hendelser/")