534ef10194
The static resources now support Gzip Compression. Software-wide support is coming with the upcoming router rewrite! Revamped Tempra Simple's topic view. We now use emojis instead of text for the fields. Experimental. Simplified the json permissions in the installer. Fixed some places where the wrong error handler is used. Fixed a bug in the word counter where it was off by one. The word count is now tracked by the topics and replies.
218 lines
4.5 KiB
Go
218 lines
4.5 KiB
Go
package main
|
|
import "log"
|
|
import "fmt"
|
|
import "time"
|
|
import "os"
|
|
import "math"
|
|
import "strings"
|
|
import "unicode"
|
|
import "encoding/base64"
|
|
import "crypto/rand"
|
|
import "net/smtp"
|
|
|
|
// Generate a cryptographically secure set of random bytes..
|
|
func GenerateSafeString(length int) (string, error) {
|
|
rb := make([]byte,length)
|
|
_, err := rand.Read(rb)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return base64.URLEncoding.EncodeToString(rb), nil
|
|
}
|
|
|
|
func relative_time(in string) (string, error) {
|
|
if in == "" {
|
|
return "", nil
|
|
}
|
|
layout := "2006-01-02 15:04:05"
|
|
t, err := time.Parse(layout, in)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
diff := time.Since(t)
|
|
hours := diff.Hours()
|
|
seconds := diff.Seconds()
|
|
weeks := int(hours / 24 / 7)
|
|
months := int(hours / 24 / 31)
|
|
switch {
|
|
case months > 11:
|
|
//return t.Format("Mon Jan 2 2006"), err
|
|
return t.Format("Jan 2 2006"), err
|
|
case months > 1:
|
|
return fmt.Sprintf("%d months ago", months), err
|
|
case months == 1:
|
|
return "a month ago", err
|
|
case weeks > 1:
|
|
return fmt.Sprintf("%d weeks ago", weeks), err
|
|
case int(hours / 24) == 7:
|
|
return "a week ago", err
|
|
case int(hours / 24) == 1:
|
|
return "1 day ago", err
|
|
case int(hours / 24) > 1:
|
|
return fmt.Sprintf("%d days ago", int(hours / 24)), err
|
|
case seconds <= 1:
|
|
return "a moment ago", err
|
|
case seconds < 60:
|
|
return fmt.Sprintf("%d seconds ago", int(seconds)), err
|
|
case seconds < 120:
|
|
return "a minute ago", err
|
|
case seconds < 3600:
|
|
return fmt.Sprintf("%d minutes ago", int(seconds / 60)), err
|
|
case seconds < 7200:
|
|
return "an hour ago", err
|
|
default:
|
|
return fmt.Sprintf("%d hours ago", int(seconds / 60 / 60)), err
|
|
}
|
|
}
|
|
|
|
func SendEmail(email string, subject string, msg string) bool {
|
|
// This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server?
|
|
if vhooks["email_send_intercept"] != nil {
|
|
return vhooks["email_send_intercept"](email, subject, msg).(bool)
|
|
}
|
|
body := "Subject: " + subject + "\n\n" + msg + "\n"
|
|
|
|
con, err := smtp.Dial(smtp_server)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
err = con.Mail(site_email)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
err = con.Rcpt(email)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
email_data, err := con.Data()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
_, err = fmt.Fprintf(email_data, body)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
err = email_data.Close()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
err = con.Quit()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func write_file(name string, content string) {
|
|
f, err := os.Create(name)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
_, err = f.WriteString(content)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
f.Sync()
|
|
f.Close()
|
|
}
|
|
|
|
func word_count(input string) (count int) {
|
|
input = strings.TrimSpace(input)
|
|
if input == "" {
|
|
return 0
|
|
}
|
|
in_space := false
|
|
for _, value := range input {
|
|
if unicode.IsSpace(value) {
|
|
if !in_space {
|
|
in_space = true
|
|
}
|
|
} else if in_space {
|
|
count++
|
|
in_space = false
|
|
}
|
|
}
|
|
return count + 1
|
|
}
|
|
|
|
func getLevel(score int) (level int) {
|
|
var base float64 = 25
|
|
var current float64
|
|
var prev float64
|
|
exp_factor := 2.8
|
|
|
|
for i := 1;;i++ {
|
|
_, bit := math.Modf(float64(i) / 10)
|
|
if bit == 0 {
|
|
exp_factor += 0.1
|
|
}
|
|
current = base + math.Pow(float64(i), exp_factor) + (prev / 3)
|
|
prev = current
|
|
if float64(score) < current {
|
|
break
|
|
}
|
|
level++
|
|
}
|
|
return level
|
|
}
|
|
|
|
func getLevelScore(getLevel int) (score int) {
|
|
var base float64 = 25
|
|
var current float64
|
|
var prev float64
|
|
var level int
|
|
exp_factor := 2.8
|
|
|
|
for i := 1;;i++ {
|
|
_, bit := math.Modf(float64(i) / 10)
|
|
if bit == 0 {
|
|
exp_factor += 0.1
|
|
}
|
|
current = base + math.Pow(float64(i), exp_factor) + (prev / 3)
|
|
prev = current
|
|
level++
|
|
if level <= getLevel {
|
|
break
|
|
}
|
|
}
|
|
return int(math.Ceil(current))
|
|
}
|
|
|
|
func getLevels(maxLevel int) []float64 {
|
|
var base float64 = 25
|
|
var current float64 = 0
|
|
var prev float64 = 0
|
|
exp_factor := 2.8
|
|
var out []float64
|
|
out = append(out, 0)
|
|
|
|
for i := 1;i <= maxLevel;i++ {
|
|
_, bit := math.Modf(float64(i) / 10)
|
|
if bit == 0 {
|
|
exp_factor += 0.1
|
|
}
|
|
current = base + math.Pow(float64(i), exp_factor) + (prev / 3)
|
|
prev = current
|
|
out = append(out, current)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func fill_forum_id_gap(biggerID int, smallerID int) {
|
|
dummy := Forum{ID:0,Name:"",Active:false,Preset:"all"}
|
|
for i := smallerID; i > biggerID;i++ {
|
|
forums = append(forums, dummy)
|
|
}
|
|
}
|
|
|
|
func fill_group_id_gap(biggerID int, smallerID int) {
|
|
dummy := Group{ID:0, Name:""}
|
|
for i := smallerID; i > biggerID;i++ {
|
|
groups = append(groups, dummy)
|
|
}
|
|
}
|