Index: /trunk/INSTANCES.md
===================================================================
--- /trunk/INSTANCES.md	(revision 69)
+++ /trunk/INSTANCES.md	(revision 70)
@@ -1,6 +1,6 @@
 # List of known instances
 
-* [tr.chaotic.ninja](https://tr.chaotic.ninja)
-    * Location: Germany
-    * Cloudflare: No
-
+| Name                                        | Cloudflare? | Country | URL                      | Engines supported |
+|---------------------------------------------|-------------|---------|--------------------------|-------------------|
+| Chaotic Ninja Communication Network Limited | No          | Germany | https://tr.chaotic.ninja | Google, Reverso   |
+|                                             |             |         |                          |                   |
Index: /trunk/README.md
===================================================================
--- /trunk/README.md	(revision 69)
+++ /trunk/README.md	(revision 70)
@@ -8,2 +8,7 @@
 ### Other projects
 * [Mozhi](https://codeberg.org/aryak/mozhi), also a fork of SimplyTranslate
+
+### Credits
+* SimpleWeb (fattalion, ManeraKai, metalune)
+* [VnPower](https://vnpower.exozy.me) ([PixivFE](https://codeberg.org/VnPower/PixivFE) developer), for most of the stuff I borrowed from his project, as we both use [Fiber](https://gofiber.io)
+* [z3bra](http://z3bra.org), for the `.ini` loading function, and privilege dropping from [Partage](http://z3bra.org/partage/)
Index: /trunk/cmd/mai/main.go
===================================================================
--- /trunk/cmd/mai/main.go	(revision 69)
+++ /trunk/cmd/mai/main.go	(revision 70)
@@ -3,5 +3,6 @@
 import (
 	"bytes"
-	"fmt"
+	"errors"
+	"log"
 	"net/http"
 	"net/url"
@@ -9,4 +10,5 @@
 	"time"
 	"runtime"
+	"strings"
 	"syscall"
 
@@ -18,4 +20,5 @@
 	"github.com/gofiber/fiber/v2/middleware/logger"
 	"github.com/gofiber/fiber/v2/middleware/limiter"
+	"github.com/gofiber/fiber/v2/middleware/recover"
 	"github.com/gofiber/template/html/v2"
 )
@@ -26,8 +29,15 @@
 )
 var conf struct {
+	danmaku int
 	listen string
 	staticpath string
 	tmplpath string
 }
+func MaiSkipLimiter(c *fiber.Ctx) bool {
+	// Paths listed here are not considered for rate limiting
+	path := c.Path()
+	return strings.HasPrefix(path, "/static") ||
+		strings.HasPrefix(path, "/docs")
+}
 func main() {
 	parseFlags()
@@ -38,4 +48,5 @@
 
 	// Default settings
+	conf.danmaku = 10
 	conf.listen = "127.0.0.1:5000"
 	conf.staticpath = "./static"
@@ -45,6 +56,5 @@
 		uid, gid, err := usergroupids(username, groupname)
 		if err != nil {
-			fmt.Println(err)
-			os.Exit(1)
+			log.Fatal(err)
 		}
 		syscall.Setuid(uid)
@@ -55,5 +65,5 @@
 	engine.AddFunc("inc", func(i int) int { return i + 1 })
 
-	app := fiber.New(
+	server := fiber.New(
 		fiber.Config{
 			AppName: "Mai",
@@ -62,7 +72,17 @@
 			ServerHeader: "Mai (using Fiber v2.x)",
 			Views: engine,
-	})
-
-	app.Use(favicon.New(
+			ErrorHandler: func(c *fiber.Ctx, err error) error {
+				code := fiber.StatusInternalServerError
+				err = c.Status(code).Render("pages/error", fiber.Map{"Title": "Error", "Error": err})
+				if err != nil {
+					return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
+				}
+				return nil
+			},
+		})
+
+	server.Use(recover.New())
+
+	server.Use(favicon.New(
 		favicon.Config{
 			File: conf.staticpath + "/favicon.ico",
@@ -70,22 +90,25 @@
 	))
 
-	app.Use(logger.New(
+	server.Use(logger.New(
 		logger.Config{
 			Format: "==> ${ip}:${port} ${status} - ${method} ${path}\n",
 			DisableColors: true,
 			Output: os.Stdout,
+			Next: MaiSkipLimiter,
 		},
 	))
 
-	app.Use(limiter.New(limiter.Config{
-		Max: 10,
-		Expiration: 180 * time.Second,
+	server.Use(limiter.New(limiter.Config{
+		Next: MaiSkipLimiter,
+		Max: conf.danmaku,
+		Expiration: 30 * time.Second,
+		LimiterMiddleware: limiter.SlidingWindow{},
 		LimitReached: func(c *fiber.Ctx) error {
-			return c.SendStatus(429)
-			return c.SendFile(conf.tmplpath + "/429.html")
+			log.Println("Limit reached!")
+			return errors.New("You're firing way too many danmaku really fast!")
 		},
 	}))
 
-	app.All("/", func(c *fiber.Ctx) error {
+	server.All("/", func(c *fiber.Ctx) error {
 		engine := c.Cookies("engine")
 		if c.Query("engine") != "" {
@@ -186,5 +209,5 @@
 	})
 
-	app.All("/api/translate", func(c *fiber.Ctx) error {
+	server.All("/api/translate", func(c *fiber.Ctx) error {
 		from := ""
 		to := ""
@@ -217,5 +240,5 @@
 	})
 
-	app.Get("/api/source_languages", func(c *fiber.Ctx) error {
+	server.Get("/api/source_languages", func(c *fiber.Ctx) error {
 		engine := c.Query("engine")
 		if _, ok := engines.Engines[engine]; !ok || engine == "" {
@@ -229,5 +252,5 @@
 	})
 
-	app.Get("/api/target_languages", func(c *fiber.Ctx) error {
+	server.Get("/api/target_languages", func(c *fiber.Ctx) error {
 		engine := c.Query("engine")
 		if _, ok := engines.Engines[engine]; !ok || engine == "" {
@@ -241,5 +264,5 @@
 	})
 
-	app.Get("/api/tts", func(c *fiber.Ctx) error {
+	server.Get("/api/tts", func(c *fiber.Ctx) error {
 		engine := c.Query("engine")
 		if _, ok := engines.Engines[engine]; !ok || engine == "" {
@@ -268,8 +291,15 @@
 		}
 	})
-	app.Get("/robots.txt", func(c *fiber.Ctx) error {
+
+	server.Get("/robots.txt", func(c *fiber.Ctx) error {
 		return c.SendString("User-Agent: *\nDisallow: /\n")
 	})
-	app.Get("/version", func(c *fiber.Ctx) error {
+
+	server.Get("/toomanyrequests", func(c *fiber.Ctx) error {
+		return c.SendFile(conf.tmplpath + "/429.html")
+		return c.SendStatus(429)
+	})
+
+	server.Get("/version", func(c *fiber.Ctx) error {
 		return c.JSON(fiber.Map{
 			"fiberversion": fiber.Version,
@@ -278,5 +308,6 @@
 		})
 	})
-	app.Post("/switchlanguages", func(c *fiber.Ctx) error {
+
+	server.Post("/switchlanguages", func(c *fiber.Ctx) error {
 		if c.Cookies("from") != "" {
 			fromCookie := new(fiber.Cookie)
@@ -295,10 +326,12 @@
 		return c.Redirect("/")
 	})
-	app.Static("/static", conf.staticpath, fiber.Static{
+	server.Static("/static", conf.staticpath, fiber.Static{
 		Compress: true,
 		ByteRange: true,
 		Browse: true,
 	})
-	app.Static("/docs", "./docs", fiber.Static{})
-	app.Listen(conf.listen)
+
+	server.Static("/docs", "./docs", fiber.Static{})
+
+	server.Listen(conf.listen)
 }
Index: /trunk/cmd/mai/readconf.go
===================================================================
--- /trunk/cmd/mai/readconf.go	(revision 69)
+++ /trunk/cmd/mai/readconf.go	(revision 70)
@@ -11,4 +11,5 @@
 		return err					        
 	}
+	conf.danmaku, _ = cfg.Section("mai").Key("danmaku").Int()
 	conf.listen = cfg.Section("mai").Key("listen").String()
 	conf.staticpath = cfg.Section("mai").Key("static").String()
Index: /trunk/go.mod
===================================================================
--- /trunk/go.mod	(revision 69)
+++ /trunk/go.mod	(revision 70)
@@ -5,5 +5,5 @@
 require (
 	github.com/PuerkitoBio/goquery v1.8.1
-	github.com/gofiber/fiber/v2 v2.50.0
+	github.com/gofiber/fiber/v2 v2.52.0
 	github.com/gofiber/template/html/v2 v2.0.5
 	gopkg.in/ini.v1 v1.67.0
@@ -15,8 +15,8 @@
 	github.com/gofiber/template v1.8.2 // indirect
 	github.com/gofiber/utils v1.1.0 // indirect
-	github.com/google/uuid v1.3.1 // indirect
-	github.com/klauspost/compress v1.16.7 // indirect
+	github.com/google/uuid v1.5.0 // indirect
+	github.com/klauspost/compress v1.17.0 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
-	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/philhofer/fwd v1.1.2 // indirect
@@ -25,7 +25,7 @@
 	github.com/tinylib/msgp v1.1.8 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
-	github.com/valyala/fasthttp v1.50.0 // indirect
+	github.com/valyala/fasthttp v1.51.0 // indirect
 	github.com/valyala/tcplisten v1.0.0 // indirect
-	golang.org/x/net v0.8.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
+	golang.org/x/net v0.17.0 // indirect
+	golang.org/x/sys v0.15.0 // indirect
 )
Index: /trunk/go.sum
===================================================================
--- /trunk/go.sum	(revision 69)
+++ /trunk/go.sum	(revision 70)
@@ -6,6 +6,6 @@
 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
-github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
+github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
+github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
 github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
 github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
@@ -14,13 +14,13 @@
 github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
 github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
-github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
-github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
-github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
+github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
 github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -36,6 +36,6 @@
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
-github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
+github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
+github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
 github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
 github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
@@ -51,6 +51,6 @@
 golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
+golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -66,6 +66,6 @@
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Index: /trunk/mai.ini.5
===================================================================
--- /trunk/mai.ini.5	(revision 69)
+++ /trunk/mai.ini.5	(revision 70)
@@ -8,11 +8,14 @@
 .Ss [mai] section
 .Bl -tag -width 11n
-.It listen
+.It danmaku (int)
+Control the maximum amount of requests
+before a ratelimit happens
+.It listen (string)
 HTTP port for the server to listen.
 Default is "localhost:5000"
-.It static
+.It static (string)
 Directory where the static resources are located.
 Default is "./static"
-.It templates
+.It templates (string)
 Directory where the templates are located.
 Default is "./views"
Index: unk/views/429.html
===================================================================
--- /trunk/views/429.html	(revision 69)
+++ 	(revision )
@@ -1,21 +1,0 @@
-<!DOCTYPE HTML PUBLIC "//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-  <head>
-    <link rel="stylesheet" href="/static/style.css">
-    <title>Too many requests</title>
-  </head>
-  <body>
-    <table border="1" align="center">
-      <thead>
-	<img src="/static/displeased_mai.png">
-      </thead>
-      <tbody>
-	<p>You have triggered a 429 error</p>
-	<p>Try again later</p>
-      </tbody>
-      <tfoot>
-	<i><a href="https://deviantart.com/view/1017571065">[image source]</a></i>
-      </tfoot>
-    </table>
-  </body>
-</html>
Index: /trunk/views/pages/error.html
===================================================================
--- /trunk/views/pages/error.html	(revision 70)
+++ /trunk/views/pages/error.html	(revision 70)
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML PUBLIC "//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <style type="text/css">
+      body {
+	  background-color: #282c37;
+	  color: rgb(234, 136, 50);
+	  text-align: center;
+      }
+      a {
+	  color: rgb(34, 36, 42);
+      }
+      p {
+	  color: rgb(221, 209, 203);
+      }
+      img {
+	  height: 640px;
+	  width: 480px;
+      }
+      </style>
+    <title>Mai | Error</title>
+  </head>
+  <body>
+    <table border="1">
+      <thead>
+	<img src="/static/displeased_mai.png">
+      </thead>
+      <tbody>
+	<h2>Error</h2>
+	<p>
+	  Something has happened and these may be the following causes:
+	</p>
+	<p>
+	  <ol>
+	    <li>You might have reached the rate limit</li>
+	    <li>There was an internal server error</li>
+	    <li>Any other client error such as a 404 Not Found</li>
+	  </ol>
+	</p>
+      </tbody>
+      <tfoot>
+	<p>
+	  <a href="https://deviantart.com/view/1017571065">[image source]</a>
+	</p>
+      </tfoot>
+    </table>
+  </body>
+</html>
