Skip to content

Initial gallery implementation #358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@
<a href="https://github.com/scribble-rs/scribble.rs/actions/workflows/test-and-build.yml">
<img src="https://github.com/scribble-rs/scribble.rs/workflows/Build/badge.svg">
</a>
<a href="https://discord.gg/cE5BKP2UnE">
<img src="https://dcbadge.limes.pink/api/server/https://discord.gg/cE5BKP2UnE">
</a>
<a href="https://ko-fi.com/N4N07DNY">
<img src="https://ko-fi.com/img/githubbutton_sm.svg">
</a>
</p>

![demo](.github/demo.png)

Scribble.rs is an alternative to the web-based drawing game skribbl.io. My main
problems with skribbl.io were the ads and the fact that a disconnect would
cause you to lose your points. On top of that, the automatic word choice was
quite annoying and caused some frustration.
Scribble.rs is a free and privacy respecting pictionary game. There's no
advertisements and you don't need an account to play.

The site will not display any ads or share any data with third parties.
It is an alternative to the web-based drawing game skribbl.io.

## Play now

Expand All @@ -24,6 +28,25 @@ if no traffic is received.)
- [scribble.bixilon.de](https://scribble.bixilon.de) (community instance maintained by @Bixilon)
- [scribble.drifty.win](https://scribble.drifty.win) (community instance maintained by @driftywinds for better latency in Asia)

## Join The Discord

Feel free to join the community Discord server to find people to play, talk, get
help or whatever else: https://discord.gg/cE5BKP2UnE

## Donation

I haven't really accepted donations for a long time. But I think the project is
polished enough now to dare taking some money. Right now the hosting is very
minimal and there's no domain. The server is located in amsterdam and many
people playing seem to be from outside of europe. So it'd be nice to have a
decentralised deployment to provide a nice experience for everyone.

So donations would go towards infrastructure and a domain!

In the future I might add some fun little benefits for donators, I have no clear
vision of it yet though.

You can donate via Ko-Fi: https://ko-fi.com/biosmarcel

## Configuration

Expand Down
8 changes: 8 additions & 0 deletions fly.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@ app = "scribblers"
# "fra" is only for paying customers
primary_region = "ams"

[metrics]
port = 8080
path = "/v1/metrics"

[build]
dockerfile = "linux.Dockerfile"

[deploy]
strategy = "canary"

[env]
ROOT_URL = "https://scribblers.fly.dev"
LOBBY_SETTING_BOUNDS_MAX_MAX_PLAYERS = 100

[http_service]
internal_port = 8080
force_https = true
Expand Down
14 changes: 12 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,26 @@ require (
github.com/gofrs/uuid/v5 v5.3.0
github.com/lxzan/gws v1.8.8
github.com/mailru/easyjson v0.7.7
github.com/stretchr/testify v1.9.0
github.com/prometheus/client_golang v1.20.5
github.com/stretchr/testify v1.10.0
github.com/subosito/gotenv v1.6.0
golang.org/x/text v0.20.0
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dolthub/maphash v0.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.5 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/sys v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
40 changes: 35 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ github.com/Bios-Marcel/discordemojimap/v2 v2.0.6 h1:VjNAT59riXBTKeKEqVb83irOZJ52
github.com/Bios-Marcel/discordemojimap/v2 v2.0.6/go.mod h1:caQqGZkTnvXOLXjChOpjzXQUMy2C1Y61ImtdVzEOvss=
github.com/Bios-Marcel/go-petname v0.0.1 h1:FELp77IS2bulz77kFXUOHqRJHXoOlL0lJUf6no5S0cQ=
github.com/Bios-Marcel/go-petname v0.0.1/go.mod h1:67IdwdIEuQBRISkUQJd4b/DvOYscEo8dNpq0D2gPHoA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA=
github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
Expand All @@ -14,23 +19,48 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk=
github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E=
github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lxzan/gws v1.8.8 h1:st193ZG8qN8sSw8/g/UituFhs7etmKzS7jUqhijg5wM=
github.com/lxzan/gws v1.8.8/go.mod h1:FcGeRMB7HwGuTvMLR24ku0Zx0p6RXqeKASeMc4VYgi4=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
37 changes: 25 additions & 12 deletions internal/api/createparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strconv"
"strings"

"github.com/scribble-rs/scribble.rs/internal/config"
"github.com/scribble-rs/scribble.rs/internal/game"
"golang.org/x/text/cases"
)
Expand Down Expand Up @@ -35,28 +36,40 @@ func ParseLanguage(value string) (*game.LanguageData, string, error) {
return nil, "", errors.New("the given language doesn't match any supported language")
}

func ParseScoreCalculation(value string) (game.ScoreCalculation, error) {
toLower := strings.ToLower(strings.TrimSpace(value))
switch toLower {
case "", "chill":
return game.ChillScoring, nil
case "competitive":
return game.CompetitiveScoring, nil
}

return nil, errors.New("the given score calculation doesn't match any supported algorithm")
}

// ParseDrawingTime checks whether the given value is an integer between
// the lower and upper bound of drawing time. All other invalid
// input, including empty strings, will return an error.
func ParseDrawingTime(value string) (int, error) {
return parseIntValue(value, game.LobbySettingBounds.MinDrawingTime,
game.LobbySettingBounds.MaxDrawingTime, "drawing time")
func ParseDrawingTime(cfg *config.Config, value string) (int, error) {
return parseIntValue(value, cfg.LobbySettingBounds.MinDrawingTime,
cfg.LobbySettingBounds.MaxDrawingTime, "drawing time")
}

// ParseRounds checks whether the given value is an integer between
// the lower and upper bound of rounds played. All other invalid
// input, including empty strings, will return an error.
func ParseRounds(value string) (int, error) {
return parseIntValue(value, game.LobbySettingBounds.MinRounds,
game.LobbySettingBounds.MaxRounds, "rounds")
func ParseRounds(cfg *config.Config, value string) (int, error) {
return parseIntValue(value, cfg.LobbySettingBounds.MinRounds,
cfg.LobbySettingBounds.MaxRounds, "rounds")
}

// ParseMaxPlayers checks whether the given value is an integer between
// the lower and upper bound of maximum players per lobby. All other invalid
// input, including empty strings, will return an error.
func ParseMaxPlayers(value string) (int, error) {
return parseIntValue(value, game.LobbySettingBounds.MinMaxPlayers,
game.LobbySettingBounds.MaxMaxPlayers, "max players amount")
func ParseMaxPlayers(cfg *config.Config, value string) (int, error) {
return parseIntValue(value, cfg.LobbySettingBounds.MinMaxPlayers,
cfg.LobbySettingBounds.MaxMaxPlayers, "max players amount")
}

// ParseCustomWords checks whether the given value is a string containing comma
Expand Down Expand Up @@ -88,9 +101,9 @@ func ParseCustomWords(lowercaser cases.Caser, value string) ([]string, error) {
// ParseClientsPerIPLimit checks whether the given value is an integer between
// the lower and upper bound of maximum clients per IP. All other invalid
// input, including empty strings, will return an error.
func ParseClientsPerIPLimit(value string) (int, error) {
return parseIntValue(value, game.LobbySettingBounds.MinClientsPerIPLimit,
game.LobbySettingBounds.MaxClientsPerIPLimit, "clients per IP limit")
func ParseClientsPerIPLimit(cfg *config.Config, value string) (int, error) {
return parseIntValue(value, cfg.LobbySettingBounds.MinClientsPerIPLimit,
cfg.LobbySettingBounds.MaxClientsPerIPLimit, "clients per IP limit")
}

// ParseCustomWordsPerTurn checks whether the given value is an integer between
Expand Down
7 changes: 4 additions & 3 deletions internal/api/createparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"reflect"
"testing"

"github.com/scribble-rs/scribble.rs/internal/config"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
Expand Down Expand Up @@ -60,7 +61,7 @@ func Test_parseDrawingTime(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()

got, err := ParseDrawingTime(testCase.value)
got, err := ParseDrawingTime(&config.Default, testCase.value)
if (err != nil) != testCase.wantErr {
t.Errorf("parseDrawingTime() error = %v, wantErr %v", err, testCase.wantErr)
return
Expand Down Expand Up @@ -93,7 +94,7 @@ func Test_parseRounds(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()

got, err := ParseRounds(testCase.value)
got, err := ParseRounds(&config.Default, testCase.value)
if (err != nil) != testCase.wantErr {
t.Errorf("parseRounds() error = %v, wantErr %v", err, testCase.wantErr)
return
Expand Down Expand Up @@ -126,7 +127,7 @@ func Test_parseMaxPlayers(t *testing.T) {
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()

got, err := ParseMaxPlayers(testCase.value)
got, err := ParseMaxPlayers(&config.Default, testCase.value)
if (err != nil) != testCase.wantErr {
t.Errorf("parseMaxPlayers() error = %v, wantErr %v", err, testCase.wantErr)
return
Expand Down
6 changes: 6 additions & 0 deletions internal/api/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ import (
"strings"

"github.com/go-chi/chi/v5"
"github.com/scribble-rs/scribble.rs/internal/metrics"
)

// SetupRoutes registers the /v1/ endpoints with the http package.
func (handler *V1Handler) SetupRoutes(rootPath string, router chi.Router) {
routePrefix := "/" + path.Join(rootPath, "v1")

metrics.SetupRoute(func(metricsHandler http.HandlerFunc) {
router.Get(routePrefix+"/metrics", metricsHandler)
})
router.Get(routePrefix+"/stats", handler.getStats)

// These exist only for the public API. We version them in order to ensure
// backwards compatibility as far as possible.
router.Get(routePrefix+"/lobby", handler.getLobbies)
router.Post(routePrefix+"/lobby", handler.postLobby)

router.Get(routePrefix+"/lobby/{lobby_id}/gallery", handler.getGallery)

router.Patch(routePrefix+"/lobby/{lobby_id}", handler.patchLobby)
// The websocket is shared between the public API and the official client
router.Get(routePrefix+"/lobby/{lobby_id}/ws", handler.websocketUpgrade)
Expand Down
Loading
Loading