diff --git a/ccreate.go b/ccreate.go new file mode 100644 index 0000000..5b9eef4 --- /dev/null +++ b/ccreate.go @@ -0,0 +1,107 @@ +package main + +import ( + "fmt" + "database/sql" + "strconv" + "strings" + "regexp" + + "github.com/gin-gonic/gin" +) + +func ccreate(c *gin.Context) { + ccreateResp(c) +} + +func ccreateSubmit(c *gin.Context) { + if getContext(c).User.ID == 0 { + resp403(c) + return + } + // check registrations are enabled + if !ccreationEnabled() { + ccreateResp(c, errorMessage{T(c, "Sorry, it's not possible to create a clan at the moment. Please try again later.")}) + return + } + + // check username is valid by our criteria + username := strings.TrimSpace(c.PostForm("username")) + if !cnameRegex.MatchString(username) { + ccreateResp(c, errorMessage{T(c, "Your clans name must contain alphanumerical characters, spaces, or any of _[]-.")}) + return + } + + + // check whether name already exists + if db.QueryRow("SELECT 1 FROM clans WHERE name = ?", c.PostForm("username")). + Scan(new(int)) != sql.ErrNoRows { + ccreateResp(c, errorMessage{T(c, "A clan with that name already exists!")}) + return + } + + // check whether tag already exists + if db.QueryRow("SELECT 1 FROM clans WHERE tag = ?", c.PostForm("tag")). + Scan(new(int)) != sql.ErrNoRows { + ccreateResp(c, errorMessage{T(c, "A clan with that tag already exists!")}) + return + } + + + // recaptcha verify + + tag := "0" + if c.PostForm("tag") != "" { + tag = c.PostForm("tag") + } + + // The actual registration. + + res, err := db.Exec(`INSERT INTO clans(name, description, icon, tag) + VALUES (?, ?, ?, ?);`, + username, c.PostForm("password"), c.PostForm("email"), tag) + if err != nil { + ccreateResp(c, errorMessage{T(c, "Whoops, an error slipped in. Clan might have been created, though. I don't know.")}) + fmt.Println(err) + return + } + lid, _ := res.LastInsertId() + + db.Exec("INSERT INTO `user_clans`(user, clan, perms) VALUES (?, ?, 8);", getContext(c).User.ID, lid) + + + + addMessage(c, successMessage{T(c, "Clan created.")}) + getSession(c).Save() + c.Redirect(302, "/c/"+strconv.Itoa(int(lid))) +} + +func ccreateResp(c *gin.Context, messages ...message) { + resp(c, 200, "clans/create.html", &baseTemplateData{ + TitleBar: "Create Clan", + KyutGrill: "register.jpg", + Scripts: []string{"https://www.google.com/recaptcha/api.js"}, + Messages: messages, + FormData: normaliseURLValues(c.Request.PostForm), + }) +} + +func ccreationEnabled() bool { + var enabled bool + db.QueryRow("SELECT value_int FROM system_settings WHERE name = 'ccreation_enabled'").Scan(&enabled) + return enabled +} + +// Check User In Query Is Same As User In Y Cookie + + +func ccin(s string, ss []string) bool { + for _, x := range ss { + if x == s { + return true + } + } + return false +} + +var cnameRegex = regexp.MustCompile(`^[A-Za-z0-9 '_\[\]-]{2,15}$`) \ No newline at end of file diff --git a/clan.go b/clan.go new file mode 100644 index 0000000..03a8203 --- /dev/null +++ b/clan.go @@ -0,0 +1,302 @@ +package main + +import ( + "database/sql" + "strconv" + "fmt" + "github.com/gin-gonic/gin" + "math/rand" + "time" +) + +// TODO: replace with simple ResponseInfo containing userid +type clanData struct { + baseTemplateData + ClanID int +} + + +func leaveClan(c *gin.Context) { + i := c.Param("cid") + // login check + if getContext(c).User.ID == 0 { + resp403(c) + return + } + if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND clan = ? AND perms = 8", getContext(c).User.ID, i). + Scan(new(int)) == sql.ErrNoRows { + // check if a nigga the clan + if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND clan = ?", getContext(c).User.ID, i). + Scan(new(int)) == sql.ErrNoRows { + addMessage(c, errorMessage{T(c, "Unexpected Error...")}) + return + } + // idk how the fuck this gonna work but fuck it + + + db.Exec("DELETE FROM user_clans WHERE user = ? AND clan = ?", getContext(c).User.ID, i) + addMessage(c, successMessage{T(c, "Left clan.")}) + getSession(c).Save() + c.Redirect(302, "/c/"+i) + } else { + //check if user even in clan!!! + if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND clan = ?", getContext(c).User.ID, i). + Scan(new(int)) == sql.ErrNoRows { + addMessage(c, errorMessage{T(c, "Unexpected Error...")}) + return + } + // delete invites + db.Exec("DELETE FROM clans_invites WHERE clan = ?", i) + // delete all members out of clan :c + db.Exec("DELETE FROM user_clans WHERE clan = ?", i) + // delete clan :c + db.Exec("DELETE FROM clans WHERE id = ?", i) + + addMessage(c, successMessage{T(c, "Disbanded Clan.")}) + getSession(c).Save() + c.Redirect(302, "/clans?mode=0") + } + + +} + + +func clanPage(c *gin.Context) { + var ( + clanID int + clanName string + clanDescription string + clanIcon string + ) + + // ctx := getContext(c) + + i := c.Param("cid") + if _, err := strconv.Atoi(i); err != nil { + err := db.QueryRow("SELECT id, name, description, icon FROM clans WHERE name = ? LIMIT 1", i).Scan(&clanID, &clanName, &clanDescription, &clanIcon) + if err != nil && err != sql.ErrNoRows { + c.Error(err) + } + } else { + err := db.QueryRow(`SELECT id, name, description, icon FROM clans WHERE id = ? LIMIT 1`, i).Scan(&clanID, &clanName, &clanDescription, &clanIcon) + switch { + case err == nil: + case err == sql.ErrNoRows: + err := db.QueryRow("SELECT id, name, description, icon FROM clans WHERE name = ? LIMIT 1", i).Scan(&clanID, &clanName, &clanDescription, &clanIcon) + if err != nil && err != sql.ErrNoRows { + c.Error(err) + } + default: + c.Error(err) + } + } + + data := new(clanData) + data.ClanID = clanID + defer resp(c, 200, "clansample.html", data) + + if data.ClanID == 0 { + data.TitleBar = "Clan not found" + data.Messages = append(data.Messages, warningMessage{T(c, "That clan could not be found.")}) + return + } + + if getContext(c).User.Privileges&1 > 0 { + if db.QueryRow("SELECT 1 FROM clans WHERE clan = ?", clanID).Scan(new(string)) != sql.ErrNoRows { + var bg string + db.QueryRow("SELECT background FROM clans WHERE id = ?", clanID).Scan(&bg) + data.KyutGrill = bg + data.KyutGrillAbsolute = true + } + } + + data.TitleBar = T(c, "%s's Clan Page", clanName) + data.DisableHH = true + // data.Scripts = append(data.Scripts, "/static/profile.js") +} + +func checkCount(rows *sql.Rows) (count int) { + for rows.Next() { + err:= rows.Scan(&count) + if err != nil { + panic(err) + } + } + return count +} + +var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func randSeq(n int) string { + rand.Seed(time.Now().UnixNano()+int64(3)) + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +func createInvite(c *gin.Context) { +ctx := getContext(c) + if string(c.PostForm("password")) == "" && string(c.PostForm("email")) == "" && string(c.PostForm("tag")) == "" && string(c.PostForm("bg")) == "" { + + + if ctx.User.ID == 0 { + resp403(c) + return + } + // big perms check lol ok + var perms int + db.QueryRow("SELECT perms FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&perms) + // delete old invite + var clan int + db.QueryRow("SELECT clan FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&clan) + if clan == 0 { + resp403(c) + return + } + + db.Exec("DELETE FROM clans_invites WHERE clan = ?", clan) + + var s string + + s = randSeq(8) + + db.Exec("INSERT INTO clans_invites(clan, invite) VALUES (?, ?)", clan, s) + } else { + // big perms check lol ok + var perms int + db.QueryRow("SELECT perms FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&perms) + // delete old invite + var clan int + db.QueryRow("SELECT clan FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1", ctx.User.ID).Scan(&clan) + if clan == 0 { + resp403(c) + return + } + + tag := "0" + if c.PostForm("tag") != "" { + tag = c.PostForm("tag") + } + + if db.QueryRow("SELECT 1 FROM clans WHERE tag = ? AND id != ?", c.PostForm("tag"), clan). + Scan(new(int)) != sql.ErrNoRows { + resp403(c) + addMessage(c, errorMessage{T(c, "A clan with that tag already exists...")}) + return + } + + db.Exec("UPDATE clans SET description = ?, icon = ?, tag = ?, background = ? WHERE id = ?", c.PostForm("password"), c.PostForm("email"), tag, c.PostForm("bg"), clan) + } + addMessage(c, successMessage{T(c, "Success")}) + getSession(c).Save() + c.Redirect(302, "/settings/clansettings") +} + + +func clanInvite(c *gin.Context) { + i := c.Param("inv") + + res := resolveInvite(i) + s := strconv.Itoa(res) + if res != 0 { + + // check if a nigga logged in + if getContext(c).User.ID == 0 { + resp403(c) + return + } + + // restricted stuff + if getContext(c).User.Privileges & 1 != 1 { + resp403(c) + return + } + + // check if clan even exists? + if db.QueryRow("SELECT 1 FROM clans WHERE id = ?", res). + Scan(new(int)) == sql.ErrNoRows { + + addMessage(c, errorMessage{T(c, "Clan doesn't exist.")}) + getSession(c).Save() + c.Redirect(302, "/c/"+s) + return + } + // check if a nigga in a clan already + if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ?", getContext(c).User.ID). + Scan(new(int)) != sql.ErrNoRows { + + addMessage(c, errorMessage{T(c, "Seems like you're already in a Clan")}) + getSession(c).Save() + c.Redirect(302, "/c/"+s) + return + } + + // idk how the fuck this gonna work but fuck it + var count int + var limit int + // members check + db.QueryRow("SELECT COUNT(*) FROM user_clans WHERE clan = ? ", res).Scan(&count) + db.QueryRow("SELECT mlimit FROM clans WHERE id = ? ", res).Scan(&limit) + if count >= limit { + addMessage(c, errorMessage{T(c, "Sorry, this clan is full.")}) + getSession(c).Save() + c.Redirect(302, "/c/"+s) + return + } + // join + db.Exec("INSERT INTO `user_clans`(user, clan, perms) VALUES (?, ?, 1);", getContext(c).User.ID, res) + addMessage(c, successMessage{T(c, "Joined clan.")}) + getSession(c).Save() + c.Redirect(302, "/c/"+s) + } else { + resp403(c) + addMessage(c, errorMessage{T(c, "nah nigga")}) + } +} + +func clanKick(c *gin.Context) { + if getContext(c).User.ID == 0 { + resp403(c) + return + } + + if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND perms = 8", getContext(c).User.ID). + Scan(new(int)) == sql.ErrNoRows { + resp403(c) + return + } + + member, err := strconv.ParseInt(c.PostForm("member"), 10, 32) + if err != nil { + fmt.Println(err) + } + if member == 0 { + resp403(c) + return + } + + if db.QueryRow("SELECT 1 FROM user_clans WHERE user = ? AND perms = 1", member). + Scan(new(int)) == sql.ErrNoRows { + resp403(c) + return + } + + db.Exec("DELETE FROM user_clans WHERE user = ?", member) + addMessage(c, successMessage{T(c, "Success.")}) + getSession(c).Save() + c.Redirect(302, "/settings/clansettings") +} + +func resolveInvite(c string)(int) { + var clanid int + row := db.QueryRow("SELECT clan FROM clans_invites WHERE invite = ?", c) + err := row.Scan(&clanid) + + if err != nil { + fmt.Println(err) + } + fmt.Println(clanid) + return clanid +} \ No newline at end of file diff --git a/main.go b/main.go index f3fc990..92087b9 100644 --- a/main.go +++ b/main.go @@ -278,6 +278,10 @@ func generateEngine() *gin.Engine { r.GET("/register/verify", verifyAccount) r.GET("/register/welcome", welcome) + r.GET("/clans/create", ccreate) + r.POST("/clans/create", ccreateSubmit) + r.GET("/c/:cid", clanPage) + r.GET("/u/:user", userProfile) r.GET("/b/:bid", beatmapInfo) diff --git a/templates/clan_group.html b/templates/clan_group.html new file mode 100644 index 0000000..4bc57f4 --- /dev/null +++ b/templates/clan_group.html @@ -0,0 +1,59 @@ +{{/*### +NoCompile=true + */}} +{{ define "clanGroup" }} +{{ with . }} +
+ {{ $teamJSON := teamJSON }} + {{ range .members }} + {{/* ignore fokabot */}} + {{ if ne (int .id) 999 }} + {{ $tj := index $teamJSON (print .id)}} +
+
+
+ Avatar +
+
+ {{ country .country false }}{{ .username }} + {{ with $tj.real_name }} +
+ {{ . }} +
+ {{ end }} + {{ with $tj.role }} +
+ {{ . }} +
+ {{ end }} +
+
+
+ + {{ time .registered_on }} +
+
+ + {{ time .latest_activity }} +
+
+ {{ if or $tj.twitter $tj.mail $tj.github }} +
+
+ {{ range $k, $v := $tj }} + {{ if and $v (in $k "github" "twitter" "mail") }} + + + + {{ end }} + {{ end }} +
+
+ {{ end }} +
+
+ {{ end }} + {{ end }} +
+ {{ end }} + {{ end }} \ No newline at end of file diff --git a/templates/clan_members.html b/templates/clan_members.html new file mode 100644 index 0000000..3b5eebc --- /dev/null +++ b/templates/clan_members.html @@ -0,0 +1,41 @@ +{{/*### +NoCompile=true +*/}} +{{ define "clanMembers" }} + {{ with . }} +
+ {{ $teamJSON := teamJSON }} + {{ range .members }} + {{/* ignore fokabot */}} + {{ if ne (int .id) 999 }} + {{ $tj := index $teamJSON (print .id)}} +
+
+
+ Avatar +
+
+ {{ country .country false }}{{ .username }} + {{ with $tj.real_name }} +
+ {{ . }} +
+ {{ end }} +
+
+
+ + {{ time .registered_on }} +
+
+ + {{ time .latest_activity }} +
+
+
+
+ {{ end }} + {{ end }} +
+ {{ end }} + {{ end }} \ No newline at end of file diff --git a/templates/clanboard.html b/templates/clanboard.html new file mode 100644 index 0000000..bfc6d54 --- /dev/null +++ b/templates/clanboard.html @@ -0,0 +1,44 @@ +{{/*### +Handler=/clans +TitleBar=Clans +KyutGrill=leaderboard2.jpg +*/}} + {{ define "tpl" }} +
+ {{ $favMode := _or (atoi (.Gin.Query "mode")) }} + + + + + {{ template "simplepag" 4 }} + + + + + + +
{{ .T "Rank" }} {{ .T "Clan" }} {{ .T "PP/Score" }} {{ .T "Playcount" }}
+ + + {{ range (.Get "clans/stats/all?m=$d" $favMode).clans }} + {{ if .name }} + + + + + + + {{end}} + {{end}} + +
#{{ .rank }} {{ .name }}{{ .chosen_mode.pp }}pp ({{ humanize .chosen_mode.total_score }}){{ .chosen_mode.playcount }}
+
+ {{ end }} \ No newline at end of file diff --git a/templates/clans/create.html b/templates/clans/create.html new file mode 100644 index 0000000..4ec7870 --- /dev/null +++ b/templates/clans/create.html @@ -0,0 +1,48 @@ +{{ define "tpl" }} +
+ {{ $regEnabled := qb "SELECT value_int FROM system_settings WHERE name = 'ccreation_enabled'" }} + {{ $isClan := qb "SELECT user, clan FROM user_clans WHERE user = ?" .Context.User.ID }} + {{ if not .Context.User.ID }} +
+ {{ .T "You need to login!" }} +
+ {{ else if not ($regEnabled.value_int.Bool) }} +
+ {{ .T "Sorry, it's not possible to create a clan at the moment. Please try again later." }} +
+ {{ else if ($isClan) }} +
+ {{ .T "You're already in a Clan." }} +
+ {{ else }} +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {{ ieForm .Gin }} +
+
+
+ +
+
+
+ {{ end }} +
+{{ end }} \ No newline at end of file diff --git a/templates/clansample.html b/templates/clansample.html new file mode 100644 index 0000000..c47c9a2 --- /dev/null +++ b/templates/clansample.html @@ -0,0 +1,128 @@ +{{/*### +Include=clan_members.html + */}} +{{ define "tpl" }} +
+ + {{ if .ClanID }} + {{ $gqm := .Gin.Query "mode" }} + {{ $global := . }} + {{ $favouritemode := $gqm }} + {{ range (.Get "clans?id=%d" .ClanID).clans }} +
+
+ {{ if .icon }} +
+ icon +
+ {{ end }} +
+

+ + {{ $global.T "%s" .name | html }} +

+
+ {{ $global.T "(%s)" (.tag | htmlescaper) | html }} +
+
+
+
+ +
+
+
+
+ + {{ $global.T "%s" (.description | htmlescaper) | html }} + {{ end }} +

+ + {{ with (.Get "clans/stats?id=%d&m=%d" .ClanID (.Gin.Query "mode"))}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{end}} + {{ range (.Get "clans?id=%d" .ClanID).clans }} + +
{{ $global.T "Global Rank" }}#{{ humanize .rank }}
{{ $global.T "PP" }}{{ humanize .chosen_mode.pp }}
{{ $global.T "Ranked Score" }}{{ humanize .chosen_mode.ranked_score }}
{{ $global.T "Total Score" }}{{ humanize .chosen_mode.total_score }}
{{ $global.T "Total Playcount" }}{{ humanize .chosen_mode.playcount }}
{{ $global.T "Total Replays Watched" }}{{ humanize .chosen_mode.replays_watched }}
{{ $global.T "Total Hits" }}{{ humanize .chosen_mode.total_hits }}
{{ end }} +
+
+ {{ if $global.Context.User.ID }} + {{ $d := qb "SELECT user, clan, perms FROM user_clans WHERE user = ? LIMIT 1" .Context.User.ID }} + {{ $p := qb "SELECT user, clan, perms FROM user_clans WHERE user = ? AND perms = 8 LIMIT 1" .Context.User.ID }} + {{ $tc := qb "SELECT user, clan, perms FROM user_clans WHERE user = ? AND clan = ? LIMIT 1" .Context.User.ID .ClanID }} + {{ $uc := or $d.clan.Int -1 }} + {{ if $d }} + {{ if $tc }} + {{ if $p }} +
+ {{ ieForm .Gin }} +
+ + {{ else }} +
+ {{ ieForm .Gin }} +
+ + {{ end }} + {{ else }} + + {{end}} + {{ else }} + + {{ end }} + {{ else }} + + + {{ end }} +

{{ .T "Clan Owner" }}

+

+ {{ .T "The leader of the clan." }}
+

+ {{ template "clanMembers" (.Get "clans/members?id=%d&r=%d" .ClanID 8) }} +
+
+
+
+

{{ .T "Members" }}

+

+ {{ .T "The members of the clan." }}
+

+ {{ template "clanMembers" (.Get "clans/members?id=%d&r=%d" .ClanID 1) }} +
+
+ {{ end }} +
+ {{ end }} \ No newline at end of file