You've already forked nyx
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:
@@ -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 {
|
||||
|
@@ -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
61
http/board/imageparser.go
Normal 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
|
||||
}
|
@@ -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)
|
||||
|
@@ -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
72
http/board/replyparser.go
Normal 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
|
||||
}
|
@@ -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>
|
||||
|
Reference in New Issue
Block a user