0
0
mirror of https://github.com/rls-moe/nyx synced 2025-08-19 06:18:38 +00:00

Improved stability, improved spam algorithm, fixed some bugs

This commit is contained in:
Tim Schuster
2017-03-15 09:13:15 +01:00
parent afd9ae71cf
commit 19d0e9282d
37 changed files with 2099 additions and 255 deletions

View File

@@ -18,18 +18,21 @@ func serveBoard(w http.ResponseWriter, r *http.Request) {
ctx := middle.GetBaseCtx(r)
err := db.View(func(tx *buntdb.Tx) error {
bName := chi.URLParam(r, "board")
log.Println("Getting board")
b, err := resources.GetBoard(tx, r.Host, bName)
if err != nil {
return err
}
ctx["Board"] = b
log.Println("Listing Threads...")
threads, err := resources.ListThreads(tx, r.Host, bName)
if err != nil {
return err
}
log.Println("Number of Thread on board: ", len(threads))
log.Println("Filling threads")
for k := range threads {
err := resources.FillReplies(tx, r.Host, threads[k])
if err != nil {

View File

@@ -27,9 +27,6 @@ var box = riceConf.MustFindBox("http/board/res/")
var (
tmpls = template.New("base")
//dirTmpl = template.New("board/dir")
//boardTmpl = template.New("board/board")
//threadTmpl = template.New("board/thread")
hdlFMap = template.FuncMap{
"renderText": resources.OperateReplyText,
@@ -53,6 +50,10 @@ var (
"formatDate": func(date time.Time) string {
return date.Format("02 Jan 06 15:04:05")
},
"isAdminSession": middle.IsAdminSession,
"isModSession": middle.IsModSession,
"captchaProb": resources.CaptchaProb,
"percentFloat": func(in float64) float64 { return in * 100 },
}
)
@@ -93,6 +94,7 @@ func Router(r chi.Router) {
func serveThumb(w http.ResponseWriter, r *http.Request) {
dat := bytes.NewBuffer([]byte{})
var date time.Time
db := middle.GetDB(r)
err := db.View(func(tx *buntdb.Tx) error {
bName := chi.URLParam(r, "board")
@@ -113,17 +115,19 @@ func serveThumb(w http.ResponseWriter, r *http.Request) {
if err != nil {
return err
}
date = resources.DateFromId(reply.ID)
return nil
})
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
http.ServeContent(w, r, "thumb.png", time.Now(), bytes.NewReader(dat.Bytes()))
http.ServeContent(w, r, "thumb.png", date, bytes.NewReader(dat.Bytes()))
}
func serveFullImage(w http.ResponseWriter, r *http.Request) {
dat := bytes.NewBuffer([]byte{})
var date time.Time
db := middle.GetDB(r)
err := db.View(func(tx *buntdb.Tx) error {
bName := chi.URLParam(r, "board")
@@ -144,13 +148,14 @@ func serveFullImage(w http.ResponseWriter, r *http.Request) {
if err != nil {
return err
}
date = resources.DateFromId(reply.ID)
return nil
})
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
http.ServeContent(w, r, "image.png", time.Now(), bytes.NewReader(dat.Bytes()))
http.ServeContent(w, r, "image.png", date, bytes.NewReader(dat.Bytes()))
}
func serveDir(w http.ResponseWriter, r *http.Request) {

61
http/board/imageparser.go Normal file
View File

@@ -0,0 +1,61 @@
package board
import (
"bytes"
"github.com/nfnt/resize"
"go.rls.moe/nyx/http/errw"
"go.rls.moe/nyx/resources"
"image"
"image/png"
"io"
"log"
"mime/multipart"
"net/http"
)
func parseImage(reply *resources.Reply, file multipart.File, hdr *multipart.FileHeader, err error) error {
if err != nil && err != http.ErrMissingFile {
return err
}
if err == http.ErrMissingFile {
return nil
}
cfg, _, err := image.DecodeConfig(file)
if err != nil {
return err
}
if cfg.Height > 8000 || cfg.Width > 8000 {
log.Println("Somebody tried to detonate the memory!")
return errw.MakeErrorWithTitle("Too large", "Your upload was too large")
}
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return err
}
img, _, err := image.Decode(file)
if err != nil {
return err
}
if img.Bounds().Dx() > 8000 || img.Bounds().Dy() > 8000 {
log.Println("Somebody tried to detonate the memory!")
return errw.MakeErrorWithTitle("Too large", "Your upload was too large")
}
thumb := resize.Thumbnail(128, 128, img, resize.Lanczos3)
imgBuf := bytes.NewBuffer([]byte{})
err = png.Encode(imgBuf, thumb)
if err != nil {
return err
}
log.Printf("Thumb has size %d KiB", imgBuf.Len()/1024)
reply.Thumbnail = make([]byte, imgBuf.Len())
copy(reply.Thumbnail, imgBuf.Bytes())
imgBuf.Reset()
err = png.Encode(imgBuf, img)
if err != nil {
return err
}
log.Printf("Image has size %d KiB", imgBuf.Len()/1024)
reply.Image = make([]byte, imgBuf.Len())
copy(reply.Image, imgBuf.Bytes())
return nil
}

View File

@@ -1,20 +1,15 @@
package board
import (
"bytes"
"fmt"
"github.com/nfnt/resize"
"github.com/pressly/chi"
"github.com/tidwall/buntdb"
"go.rls.moe/nyx/http/errw"
"go.rls.moe/nyx/http/middle"
"go.rls.moe/nyx/resources"
"image"
_ "image/gif"
_ "image/jpeg"
"image/png"
"net/http"
"strconv"
)
func handleNewReply(w http.ResponseWriter, r *http.Request) {
@@ -23,7 +18,7 @@ func handleNewReply(w http.ResponseWriter, r *http.Request) {
errw.ErrorWriter(err, w, r)
return
}
err = r.ParseMultipartForm(10 * 1024 * 1024)
err = r.ParseMultipartForm(4 * 1024 * 1024)
if err != nil {
errw.ErrorWriter(err, w, r)
return
@@ -39,66 +34,16 @@ func handleNewReply(w http.ResponseWriter, r *http.Request) {
var reply = &resources.Reply{}
reply.Board = chi.URLParam(r, "board")
tid, err := strconv.Atoi(chi.URLParam(r, "thread"))
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
reply.Thread = tid
reply.Text = r.FormValue("text")
if len(reply.Text) > 10000 {
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are too many characters"), w, r)
return
}
if len(reply.Text) < 5 {
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are not enough characters"), w, r)
return
}
if score, err := resources.SpamScore(reply.Text); err != nil || !resources.CaptchaPass(score) {
err = parseReply(r, reply)
if err == trollThrottle {
http.Redirect(w, r,
fmt.Sprintf("/%s/%s/thread.html?err=trollthrottle",
chi.URLParam(r, "board"), chi.URLParam(r, "thread")),
http.StatusSeeOther)
return
}
{
file, _, err := r.FormFile("image")
if err != nil && err != http.ErrMissingFile {
errw.ErrorWriter(err, w, r)
return
} else if err != http.ErrMissingFile {
img, _, err := image.Decode(file)
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
thumb := resize.Thumbnail(128, 128, img, resize.Lanczos3)
imgBuf := bytes.NewBuffer([]byte{})
err = png.Encode(imgBuf, img)
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
fmt.Println("Image has size ", len(imgBuf.Bytes()))
reply.Image = imgBuf.Bytes()
imgBuf = bytes.NewBuffer([]byte{})
err = png.Encode(imgBuf, thumb)
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
reply.Thumbnail = imgBuf.Bytes()
}
}
reply.Metadata = map[string]string{}
if r.FormValue("tripcode") != "" {
reply.Metadata["trip"] = resources.CalcTripCode(r.FormValue("tripcode"))
} else {
reply.Metadata["trip"] = "Anonymous"
} else if err != nil {
errw.ErrorWriter(err, w, r)
return
}
db := middle.GetDB(r)

View File

@@ -1,16 +1,12 @@
package board
import (
"bytes"
"fmt"
"github.com/nfnt/resize"
"github.com/pressly/chi"
"github.com/tidwall/buntdb"
"go.rls.moe/nyx/http/errw"
"go.rls.moe/nyx/http/middle"
"go.rls.moe/nyx/resources"
"image"
"image/png"
"net/http"
)
@@ -37,59 +33,18 @@ func handleNewThread(w http.ResponseWriter, r *http.Request) {
var thread = &resources.Thread{}
var mainReply = &resources.Reply{}
mainReply.Board = chi.URLParam(r, "board")
thread.Board = chi.URLParam(r, "board")
mainReply.Text = r.FormValue("text")
if len(mainReply.Text) > 10000 {
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are too many characters"), w, r)
return
}
if len(mainReply.Text) < 5 {
errw.ErrorWriter(errw.MakeErrorWithTitle("I'm sorry but I can't do that", "These are not enough characters"), w, r)
return
}
if score, err := resources.SpamScore(mainReply.Text); err != nil || !resources.CaptchaPass(score) {
err = parseReply(r, mainReply)
if err == trollThrottle {
http.Redirect(w, r,
fmt.Sprintf("/%s/board.html?err=trollthrottle",
chi.URLParam(r, "board")),
http.StatusSeeOther)
return
}
{
file, _, err := r.FormFile("image")
if err != nil && err != http.ErrMissingFile {
errw.ErrorWriter(err, w, r)
return
} else if err != http.ErrMissingFile {
img, _, err := image.Decode(file)
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
thumb := resize.Thumbnail(128, 128, img, resize.Lanczos3)
imgBuf := bytes.NewBuffer([]byte{})
err = png.Encode(imgBuf, img)
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
fmt.Println("Image has size ", len(imgBuf.Bytes()))
mainReply.Image = imgBuf.Bytes()
imgBuf = bytes.NewBuffer([]byte{})
err = png.Encode(imgBuf, thumb)
if err != nil {
errw.ErrorWriter(err, w, r)
return
}
mainReply.Thumbnail = imgBuf.Bytes()
}
}
mainReply.Metadata = map[string]string{}
if r.FormValue("tripcode") != "" {
mainReply.Metadata["trip"] = resources.CalcTripCode(r.FormValue("tripcode"))
} else if err != nil {
errw.ErrorWriter(err, w, r)
return
}
db := middle.GetDB(r)

72
http/board/replyparser.go Normal file
View File

@@ -0,0 +1,72 @@
package board
import (
"errors"
"fmt"
"github.com/pressly/chi"
"go.rls.moe/nyx/http/errw"
"go.rls.moe/nyx/http/middle"
"go.rls.moe/nyx/resources"
"net/http"
"strconv"
)
var trollThrottle = errors.New("Troll throttle")
func parseReply(r *http.Request, reply *resources.Reply) error {
reply.Board = chi.URLParam(r, "board")
reply.Text = r.FormValue("text")
if tidStr := chi.URLParam(r, "thread"); tidStr != "" {
var err error
reply.Thread, err = strconv.Atoi(tidStr)
if err != nil {
return err
}
}
if len(reply.Text) > 10000 {
return errw.MakeErrorWithTitle(
"I'm sorry but I can't do that",
"There are too many characters")
}
if len(reply.Text) < 5 {
return errw.MakeErrorWithTitle(
"I'm sorry but I can't do that",
"There are not enough characters")
}
reply.Metadata = map[string]string{}
spamScore, err := resources.SpamScore(reply.Text)
if err != nil {
return err
}
reply.Metadata["spamscore"] = fmt.Sprintf("%.6f", spamScore)
reply.Metadata["captchaprob"] = fmt.Sprintf("%.2f", resources.CaptchaProb(spamScore)*100)
if !resources.CaptchaPass(spamScore) {
return trollThrottle
}
file, hdr, err := r.FormFile("image")
err = parseImage(reply, file, hdr, err)
if err != nil {
return err
}
if r.FormValue("tripcode") != "" {
reply.Metadata["trip"] = resources.CalcTripCode(r.FormValue("tripcode"))
}
if middle.IsModSession(middle.GetSession(r)) {
if r.FormValue("modpost") != "" {
reply.Metadata["modpost"] = "yes"
}
if middle.IsAdminSession(middle.GetSession(r)) {
if r.FormValue("adminpost") != "" {
reply.Metadata["adminpost"] = "yes"
}
}
}
return nil
}

View File

@@ -78,6 +78,23 @@
</td>
</tr>
{{ end }}
{{ if (isModSession .Session) }}
<tr>
<td class="postblock">
Mod Post
</td>
<td>
<label>
<input type="checkbox" name="modpost"/>Mark as Mod Post
</label>
{{ if (isAdminSession .Session) }}
<label>
<input type="checkbox" name="adminpost"/>Mark as Admin Post
</label>
{{ end }}
</td>
</tr>
{{ end }}
<tr>
<td class="postblock">
@@ -100,6 +117,12 @@
{{ else }}
Anonymous
{{ end }}
{{ if .Reply.Metadata.modpost }}
(Mod)
{{ end }}
{{ if .Reply.Metadata.adminpost }}
[Admin]
{{ end }}
</span></label>
<span class="date">{{dateFromID .Reply.ID | formatDate}}</span>
{{ if .Session }}
@@ -126,7 +149,15 @@
{{ end }}
{{ end }}
<span>
{{printf "[SpamScore: %f]" (rateSpam .Reply.Text) }}
{{ if not .Reply.Metadata.spamscore }}
{{ $score := (rateSpam .Reply.Text) }}
{{printf "[SpamScore: %f]" $score }}
{{printf "[Captcha: %.3f%%]" (percentFloat (captchaProb $score)) }}
{{printf "[OLD]"}}
{{ else }}
{{ printf "[SpamScore: %s]" .Reply.Metadata.spamscore }}
{{ printf "[Captcha: %s %%]" .Reply.Metadata.captchaprob }}
{{ end }}
</span>
<span class="reflink">
<a href="/{{.Boardlink}}/{{.ThreadID}}/thread.html">No.{{.Reply.ID}}</a>