diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..de0c700b9fdfd6c4df734a93f0d4394ade5bfc92
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+server.conf
diff --git a/cleanup.go b/cleanup.go
new file mode 100644
index 0000000000000000000000000000000000000000..6e7957e59c96a1f0511acd26cb4735666a6fbaf7
--- /dev/null
+++ b/cleanup.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"git.randomchars.net/freenitori/log"
+	"os"
+)
+
+func cleanup() {
+	// Close session
+	if err := session.Close(); err != nil {
+		log.Fatalf("Error closing session, %s", err)
+		os.Exit(1)
+	}
+}
diff --git a/command.go b/command.go
new file mode 100644
index 0000000000000000000000000000000000000000..1b5f242ed72f76f7fb8b24f4458c3b9c92a3c5d1
--- /dev/null
+++ b/command.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+	"git.randomchars.net/freenitori/log"
+	"git.randomchars.net/freenitori/multiplexer"
+)
+
+func init() {
+	m.Route(&multiplexer.Route{
+		Pattern:       "ping",
+		AliasPatterns: []string{"pong"},
+		Description:   "ping pong.",
+		Category:      system,
+		Handler: func(context *multiplexer.Context) {
+			err := Database.Set("key", "hello world")
+			if err != nil {
+				log.Info(err)
+			}
+			context.Session.ChannelMessageSend(context.Channel.ID, "Pong")
+			log.Infof("Pong! " + context.User.Username)
+		},
+	})
+
+	m.Route(&multiplexer.Route{
+		Pattern:       "get",
+		AliasPatterns: []string{"value"},
+		Description:   "database test.",
+		Category:      system,
+		Handler: func(context *multiplexer.Context) {
+			data, err := Database.Get("key2")
+			if err != nil {
+				log.Info(err)
+			}
+			context.Session.ChannelMessageSend(context.Channel.ID, "getting value " + data)
+			log.Infof("Nice! " + context.User.Username )
+		},
+	})
+}
diff --git a/config.go b/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..d996c55cd561d2975e46152b632a00d135d0079d
--- /dev/null
+++ b/config.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+	"flag"
+	"git.randomchars.net/freenitori/log"
+	"github.com/BurntSushi/toml"
+	"os"
+)
+
+var config configPayload
+var configPath string
+var defaultConfig = configPayload{
+	Prefix:           "!",
+	Token:            "TOKEN",
+}
+
+type configPayload struct {
+	Prefix           string
+	Token            string
+}
+
+func init() {
+	flag.StringVar(&configPath, "c", "server.conf", "Specify configuration file location.")
+}
+
+func parse() {
+	if _, err := toml.DecodeFile(configPath, &config); err != nil {
+		if os.IsNotExist(err) {
+			var file *os.File
+			if file, err = os.Create(configPath); err != nil {
+				log.Fatalf("Error while creating configuration file, %s", err)
+				os.Exit(1)
+			}
+			if err = toml.NewEncoder(file).Encode(defaultConfig); err != nil {
+				log.Fatalf("Error while encoding default configuration, %s", err)
+				os.Exit(1)
+			}
+			log.Warnf("Default configuration generated at %s, edit before next startup.", configPath)
+			os.Exit(1)
+		}
+		log.Fatalf("Error while decoding configuration file, %s", err)
+		os.Exit(1)
+	} else {
+		log.Infof("Loaded config at %s.", configPath)
+	}
+}
\ No newline at end of file
diff --git a/database.go b/database.go
new file mode 100644
index 0000000000000000000000000000000000000000..7c26c776c95241679710472a17f27cfc63d4b804
--- /dev/null
+++ b/database.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+	ldb "github.com/syndtr/goleveldb/leveldb"
+)
+
+var Database LevelDB
+
+type LevelDB struct {
+	database *ldb.DB
+}
+
+func (db *LevelDB) Open(path string) error {
+	if instance, err := ldb.OpenFile(path, nil); err != nil {
+		return err
+	} else {
+		db.database = instance
+	}
+	return nil
+}
+
+func (db *LevelDB) Get(key string) (string, error) {
+
+	data , err := db.database.Get([]byte(key), nil)
+
+		if err != nil {
+			return string(data), err
+		}
+
+		return string(data), err
+}
+
+func (db *LevelDB) Set(key, value string) error {
+	err := db.database.Put([]byte(key), []byte(value), nil)
+	if err != nil {
+		return err
+	} else {
+	  return nil
+	}
+}
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..a3e0a32f55f7f54c3717e2a4e6b0f61b128dcd44
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,18 @@
+module main
+
+go 1.17
+
+require (
+	git.randomchars.net/freenitori/embedutil v1.0.2 // indirect
+	git.randomchars.net/freenitori/log v1.0.0 // indirect
+	git.randomchars.net/freenitori/multiplexer v1.0.14 // indirect
+	github.com/BurntSushi/toml v0.4.1 // indirect
+	github.com/bwmarrin/discordgo v0.23.2 // indirect
+	github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
+	github.com/gorilla/websocket v1.4.0 // indirect
+	github.com/magefile/mage v1.10.0 // indirect
+	github.com/sirupsen/logrus v1.8.0 // indirect
+	github.com/syndtr/goleveldb v1.0.0 // indirect
+	golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 // indirect
+	golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..13ee06da815ba79161064c94c82933a04fc8c94e
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,41 @@
+git.randomchars.net/freenitori/embedutil v1.0.2 h1:8A3zSLkRRpslR0VG3vwtgxcEka4A163+K7UsKVhkHhs=
+git.randomchars.net/freenitori/embedutil v1.0.2/go.mod h1:ELkCpzF7OBB7Mn//AdkRNkmj+M61DPWllArBgX3A8x4=
+git.randomchars.net/freenitori/log v1.0.0 h1:hU99jGk940I1O5OcaTfnXOpN8ozXiarxhu6xpL0xe7c=
+git.randomchars.net/freenitori/log v1.0.0/go.mod h1:YZFRZgVWDIrbyDGHyDeRlIRWeq0DXamXONxIt12eq2Q=
+git.randomchars.net/freenitori/multiplexer v1.0.14 h1:j6r0dhI+VBDDog7Uc8a492vSSdu/BfiivweTF1sAFK8=
+git.randomchars.net/freenitori/multiplexer v1.0.14/go.mod h1:Bx9vu2RXDtBrsKBslrhrc8v3IJl5Dna7I/rsHF586w0=
+github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
+github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
+github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/magefile/mage v1.10.0 h1:3HiXzCUY12kh9bIuyXShaVe529fJfyqoVM42o/uom2g=
+github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/sirupsen/logrus v1.8.0 h1:nfhvjKcUMhBMVqbKHJlk5RPrrfYr/NMo3692g0dwfWU=
+github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
+github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/handler.go b/handler.go
new file mode 100644
index 0000000000000000000000000000000000000000..88558293cfc86666d49a01eb2302a20561222fa0
--- /dev/null
+++ b/handler.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"git.randomchars.net/freenitori/log"
+	"git.randomchars.net/freenitori/multiplexer"
+)
+
+func init() {
+	m.MessageCreate = append(m.MessageCreate, messageHandler)
+}
+
+func messageHandler(context *multiplexer.Context) {
+	log.Infof(context.User.Username + "#" + context.User.Discriminator + " sent the following message: " + context.Message.Content)
+}
\ No newline at end of file
diff --git a/main.go b/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..e9f6f32d904ef56156f9c4fff6ef3d4d3352c863
--- /dev/null
+++ b/main.go
@@ -0,0 +1,132 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"git.randomchars.net/freenitori/log"
+	"git.randomchars.net/freenitori/multiplexer"
+	"github.com/bwmarrin/discordgo"
+	"github.com/sirupsen/logrus"
+	"os"
+	"os/signal"
+	"syscall"
+)
+
+var (
+	session *discordgo.Session
+	m       = multiplexer.New()
+	system  = multiplexer.NewCategory("System", "System-related utilities.")
+	verbose bool
+)
+
+func init() {
+	flag.BoolVar(&verbose, "v", false, "Start up with debug logging.")
+}
+
+func main() {
+	flag.Parse()
+	parse()
+
+	if verbose {
+		log.SetLevel(logrus.DebugLevel)
+	} else {
+		log.SetLevel(logrus.InfoLevel)
+	}
+
+	// Set discordgo log handler
+	discordgo.Logger = func(msgL, _ int, format string, a ...interface{}) {
+		var level logrus.Level
+		switch msgL {
+		case discordgo.LogDebug:
+			level = logrus.DebugLevel
+		case discordgo.LogInformational:
+			level = logrus.InfoLevel
+		case discordgo.LogWarning:
+			level = logrus.WarnLevel
+		case discordgo.LogError:
+			level = logrus.ErrorLevel
+		}
+		log.Instance.Log(level, fmt.Sprintf(format, a...))
+	}
+
+	// Set command not found handler
+	multiplexer.NoCommandMatched = func(context *multiplexer.Context) {
+	}
+
+	// Configure session
+	if s, err := discordgo.New(); err != nil {
+		log.Fatalf("Error while creating session, %s", err)
+		os.Exit(1)
+	} else {
+		session = s
+	}
+	session.UserAgent = "DiscordBot (voice-bot)"
+	session.Token = "Bot " + config.Token
+	session.ShouldReconnectOnError = true
+	session.Identify.Intents = discordgo.IntentsAll
+	session.State.TrackVoice = true
+
+	// Open session
+	func() {
+	open:
+		if err := session.Open(); err != nil {
+			if session.Identify.Intents == discordgo.IntentsAll {
+				log.Warnf("Wasn't able to start with full intents, some stuff might not work (%s)", err)
+				session.Identify.Intents = discordgo.IntentsAllWithoutPrivileged
+				goto open
+			}
+
+			log.Fatalf("Error while opening session, %s", err)
+			os.Exit(1)
+		}
+	}()
+
+
+	// Setup multiplexer
+	m.SessionRegisterHandlers(session)
+	m.Prefix = config.Prefix
+	m.Categories = append(m.Categories, system)
+	err := Database.Open("/tmp/test.db")
+	if err != nil {
+		log.Info(err)
+	}
+	// Output message
+	log.Infof("Logged in as %s#%s (%s).",
+		session.State.User.Username,
+		session.State.User.Discriminator,
+		session.State.User.ID)
+	if application, err := session.Application("@me"); err != nil {
+		log.Fatalf("Unable to get application, %s", err)
+		os.Exit(1)
+	} else {
+		log.Infof("Invite URL: https://discord.com/oauth2/authorize?client_id=%s&scope=bot&permissions=8", application.ID)
+	}
+
+	// Signal handling
+	signalChannel := make(chan os.Signal, 1)
+	signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, os.Interrupt, syscall.SIGTERM)
+	var exit int
+	func() {
+		for {
+			currentSignal := <-signalChannel
+			switch currentSignal {
+			case os.Interrupt:
+				exit = 0
+				println()
+				log.Info("Gracefully exiting.")
+				return
+			default:
+				exit = 0
+				log.Info("Gracefully exiting.")
+				return
+			}
+		}
+	}()
+
+	switch exit {
+	case 0:
+		cleanup()
+	default:
+		os.Exit(exit)
+	}
+}