Skip to content

Commit

Permalink
Merge pull request #6 from havce/open
Browse files Browse the repository at this point in the history
Prepare for open sourcing
  • Loading branch information
devgianlu authored Jan 21, 2024
2 parents b98dd82 + 9b2e879 commit 74a91b8
Show file tree
Hide file tree
Showing 19 changed files with 90 additions and 70 deletions.
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ COPY . .

ENV CGO_ENABLED=0

RUN go build -o havcebotd ./cmd/havcebotd
RUN go build -o ctfbotd ./cmd/ctfbotd

FROM gcr.io/distroless/static-debian12

COPY --from=builder /app/havcebotd /havcebotd
COPY --from=builder /app/ctfbotd /ctfbotd

ENTRYPOINT ["/havcebotd", "-config-path", "/havcebotd.toml"]
ENTRYPOINT ["/ctfbotd", "-config-path", "/ctfbotd.toml"]
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# ctfbot

A Discord bot for managing CTFs in your team.

Picture this: your Discord server has grown too much and there are too many people wandering around. You are tired of
gathering participation and manually setting your channel permissions. Then this what you are looking for!

## Features

The bot supports various commands:

- `/new`: Create a new CTF (admin only)
- `/open`: Open the CTF for registration (admin only)
- `/close`: Close the CTF registration (admin only)
- `/delete`: Delete the CTF (admin only)
- `/info`: List CTFs available on CTFTime for the next weeks
- `/vote`: Start a vote for which CTF to play (admin only)
- `/chal`: Create a new challenge inside the CTF
- `/flag`: Mark the challenge as solved
- `/blood`: Mark the challenge as first blooded
22 changes: 11 additions & 11 deletions cmd/havcebotd/main.go → cmd/ctfbotd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import (
"strings"

"github.com/BurntSushi/toml"
"github.com/havce/havcebot"
"github.com/havce/havcebot/ctftime"
"github.com/havce/havcebot/discord"
"github.com/havce/havcebot/sqlite"
"github.com/havce/ctfbot"
"github.com/havce/ctfbot/ctftime"
"github.com/havce/ctfbot/discord"
"github.com/havce/ctfbot/sqlite"
)

// Build version, injected during build.
Expand All @@ -39,8 +39,8 @@ type Config struct {
}

const (
DefaultDSN = "~/havcebot.sqlite3"
DefaultConfigPath = "~/havcebot.toml"
DefaultDSN = "~/ctfbot.sqlite3"
DefaultConfigPath = "~/ctfbot.toml"
)

const (
Expand All @@ -59,8 +59,8 @@ func DefaultConfig() Config {

func main() {
// Propagate build information to root package to share globally.
havcebot.Version = strings.TrimPrefix(version, "")
havcebot.Commit = commit
ctfbot.Version = strings.TrimPrefix(version, "")
ctfbot.Commit = commit

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
defer cancel()
Expand Down Expand Up @@ -116,8 +116,8 @@ func (m *Main) Close(ctx context.Context) error {
}

func (m *Main) ParseFlagAndConfig(ctx context.Context, args []string) error {
f := flag.NewFlagSet("havcebotd", flag.ContinueOnError)
f.StringVar(&m.ConfigPath, "config-path", "~/.havcebot.toml", "config file path")
f := flag.NewFlagSet("ctfbotd", flag.ContinueOnError)
f.StringVar(&m.ConfigPath, "config-path", "~/.ctfbot.toml", "config file path")
if err := f.Parse(args); err != nil {
return err
}
Expand Down Expand Up @@ -204,7 +204,7 @@ func (m *Main) Run(ctx context.Context) (err error) {
return err
}

slog.Log(ctx, slog.LevelInfo, "havcebotd started")
slog.Log(ctx, slog.LevelInfo, "ctfbotd started")

return nil
}
Expand Down
2 changes: 1 addition & 1 deletion ctf.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package havcebot
package ctfbot

import (
"context"
Expand Down
2 changes: 1 addition & 1 deletion havcebot.go → ctfbot.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package havcebot
package ctfbot

var (
Version = "n/a"
Expand Down
2 changes: 1 addition & 1 deletion havcebot.toml.sample → ctfbot.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[db]
dsn = "/database/havcebot.sqlite"
dsn = "/database/ctfbot.sqlite"

[discord]
# Required
Expand Down
6 changes: 3 additions & 3 deletions ctftime/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"net/url"
"strconv"

"github.com/havce/havcebot"
"github.com/havce/ctfbot"
)

type Client struct {
Expand Down Expand Up @@ -42,9 +42,9 @@ func (c *Client) FindEventByID(ctx context.Context, id int) (*Event, error) {
}

if resp.StatusCode > 400 && resp.StatusCode < 499 {
return nil, havcebot.Errorf(havcebot.ENOTFOUND, "Event not found.")
return nil, ctfbot.Errorf(ctfbot.ENOTFOUND, "Event not found.")
} else if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, havcebot.Errorf(havcebot.EINVALID, "Internal server error.")
return nil, ctfbot.Errorf(ctfbot.EINVALID, "Internal server error.")
}

event := &Event{}
Expand Down
28 changes: 14 additions & 14 deletions discord/ctf.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/handler"
"github.com/disgoorg/snowflake/v2"
"github.com/havce/havcebot"
"github.com/havce/ctfbot"
)

const (
Expand All @@ -34,7 +34,7 @@ func (s *Server) handleCommandNewCTF(event *handler.CommandEvent) error {
// Check if CTF is already present with the same name.
_, err := s.CTFService.FindCTFByName(context.TODO(), ctfName)
if err == nil {
return Error(event, havcebot.Errorf(havcebot.ECONFLICT, "A CTF with the same name has already been created."))
return Error(event, ctfbot.Errorf(ctfbot.ECONFLICT, "A CTF with the same name has already been created."))
}

_, err = event.CreateFollowupMessage(discord.NewMessageCreateBuilder().
Expand Down Expand Up @@ -188,7 +188,7 @@ func (s *Server) handleCreateCTF(event *handler.ComponentEvent) error {
// Check again if CTF is already present with the same name.
_, err = s.CTFService.FindCTFByName(context.TODO(), ctf)
if err == nil {
return Error(event, havcebot.Errorf(havcebot.ECONFLICT, "A CTF with the same name has already been created."))
return Error(event, ctfbot.Errorf(ctfbot.ECONFLICT, "A CTF with the same name has already been created."))
}

// Create role with CTF name.
Expand Down Expand Up @@ -293,7 +293,7 @@ func (s *Server) handleCreateCTF(event *handler.ComponentEvent) error {
return Error(event, err)
}

err = s.CTFService.CreateCTF(context.TODO(), &havcebot.CTF{
err = s.CTFService.CreateCTF(context.TODO(), &ctfbot.CTF{
Name: ctf,
Start: time.Now(),
// Parse the role.ID as uint64 and then convert
Expand Down Expand Up @@ -338,18 +338,18 @@ func (s *Server) handleJoinCTF(event *handler.ComponentEvent) error {
}

if !retrievedCTF.CanJoin {
return Error(event, havcebot.Errorf(havcebot.EUNAUTHORIZED, "Registrations are closed for `%s`. Ask an admin if you want to join.", ctf))
return Error(event, ctfbot.Errorf(ctfbot.EUNAUTHORIZED, "Registrations are closed for `%s`. Ask an admin if you want to join.", ctf))
}

role, found := s.client.Caches().Role(*event.GuildID(), roleID)
if !found {
return Error(event,
havcebot.Errorf(havcebot.ENOTFOUND, "Couldn't find player role for `%s`. Maybe it was deleted?", ctf))
ctfbot.Errorf(ctfbot.ENOTFOUND, "Couldn't find player role for `%s`. Maybe it was deleted?", ctf))
}

if slices.Contains(event.Member().RoleIDs, role.ID) {
return Error(event,
havcebot.Errorf(havcebot.ECONFLICT, "You already joined `%s`", ctf))
ctfbot.Errorf(ctfbot.ECONFLICT, "You already joined `%s`", ctf))
}

// Add the roleID to the roleIDs of the user.
Expand All @@ -376,7 +376,7 @@ func (s *Server) handleUpdateCanJoin(canJoin bool) func(event *handler.CommandEv

// If you're not inside a CTF it will output a CTF not found error.
_, err = s.CTFService.UpdateCTF(context.TODO(), parentChannel.Name(),
havcebot.CTFUpdate{
ctfbot.CTFUpdate{
CanJoin: &canJoin,
})
if err != nil {
Expand All @@ -403,8 +403,8 @@ func (s *Server) handleFlag(blood bool) func(event *handler.CommandEvent) error
}

if !s.flagAllowed(event.Channel().Name()) {
return Error(event, havcebot.Errorf(
havcebot.EINVALID, "You cannot flag here."))
return Error(event, ctfbot.Errorf(
ctfbot.EINVALID, "You cannot flag here."))
}

// Check if someone has already flagged this.
Expand All @@ -415,7 +415,7 @@ func (s *Server) handleFlag(blood bool) func(event *handler.CommandEvent) error
blocklist := []string{flagEmoji, bloodEmoji}
// Check against blocklist.
if slices.Contains(blocklist, string(c)) {
return Error(event, havcebot.Errorf(havcebot.EINVALID, "Somebody has already %s this.", prefix))
return Error(event, ctfbot.Errorf(ctfbot.EINVALID, "Somebody has already %s this.", prefix))
}
}

Expand Down Expand Up @@ -478,8 +478,8 @@ func (s *Server) handleNewChal(event *handler.CommandEvent) error {

// If so, return an error.
if found {
return Error(event, havcebot.Errorf(
havcebot.ECONFLICT, "Somebody has already created `%s`.", chalName))
return Error(event, ctfbot.Errorf(
ctfbot.ECONFLICT, "Somebody has already created `%s`.", chalName))
}

// We already validated the existence of parentChannel in the middleware.
Expand All @@ -502,7 +502,7 @@ func (s *Server) handleNewChal(event *handler.CommandEvent) error {

role, found := s.client.Caches().Role(*event.GuildID(), roleID)
if !found {
return Error(event, havcebot.Errorf(havcebot.EINTERNAL, "Couldn't find player role for `%s`. Maybe it was deleted?", ctf.Name))
return Error(event, ctfbot.Errorf(ctfbot.EINTERNAL, "Couldn't find player role for `%s`. Maybe it was deleted?", ctf.Name))
}

// Create the channel with our custom permissions.
Expand Down
6 changes: 3 additions & 3 deletions discord/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/rest"
"github.com/havce/havcebot"
"github.com/havce/ctfbot"
)

type CreateFollowupMessager interface {
Expand All @@ -14,9 +14,9 @@ type CreateFollowupMessager interface {

func Error(event CreateFollowupMessager, err error) error {
// Extract error code and message.
code, message := havcebot.ErrorCode(err), havcebot.ErrorMessage(err)
code, message := ctfbot.ErrorCode(err), ctfbot.ErrorMessage(err)

if code == havcebot.EINTERNAL {
if code == ctfbot.EINTERNAL {
event.Client().Logger().Error("Internal server error", code, err)
}

Expand Down
8 changes: 4 additions & 4 deletions discord/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/handler"
"github.com/havce/havcebot"
"github.com/havce/havcebot/ctftime"
"github.com/havce/ctfbot"
"github.com/havce/ctfbot/ctftime"
)

const (
Expand Down Expand Up @@ -46,7 +46,7 @@ func (s *Server) handleInfoCTF(vote bool) func(event *handler.CommandEvent) erro

title := event.Title
if vote {
title = havcebot.Itoe(i+1) + " " + title
title = ctfbot.Itoe(i+1) + " " + title
}

embed := discord.Embed{
Expand Down Expand Up @@ -110,7 +110,7 @@ func (s *Server) handleInfoCTF(vote bool) func(event *handler.CommandEvent) erro
}

for i := range embeds {
err = s.client.Rest().AddReaction(event.Channel().ID(), msg.ID, havcebot.Itoe(i+1))
err = s.client.Rest().AddReaction(event.Channel().ID(), msg.ID, ctfbot.Itoe(i+1))
if err != nil {
return Error(event, err)
}
Expand Down
6 changes: 3 additions & 3 deletions discord/middlewares.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgo/handler"
"github.com/disgoorg/disgo/handler/middleware"
"github.com/havce/havcebot"
"github.com/havce/ctfbot"
)

// AdminOnly restricts access to the routes to Administrators only.
Expand All @@ -24,7 +24,7 @@ var AdminOnly handler.Middleware = func(next handler.Handler) handler.Handler {
SetEphemeral(true).
SetEmbeds(messageEmbedError("You're not authorized to run this command.")).Build())

return havcebot.Errorf(havcebot.EUNAUTHORIZED, "You're not authorized to run this command.")
return ctfbot.Errorf(ctfbot.EUNAUTHORIZED, "You're not authorized to run this command.")
}
}

Expand All @@ -35,7 +35,7 @@ func (s *Server) MustBeInsideCTFAndAdmin(next handler.Handler) handler.Handler {
discord.NewMessageCreateBuilder().
SetEphemeral(true).
SetEmbeds(messageEmbedError("You're not authorized to run this command.")).Build())
return havcebot.Errorf(havcebot.EUNAUTHORIZED, "You're not authorized to run this command.")
return ctfbot.Errorf(ctfbot.EUNAUTHORIZED, "You're not authorized to run this command.")
}
return s.MustBeInsideCTF(next)(e)
}
Expand Down
6 changes: 3 additions & 3 deletions discord/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"github.com/disgoorg/disgo/handler"
"github.com/disgoorg/disgo/handler/middleware"
"github.com/disgoorg/snowflake/v2"
"github.com/havce/havcebot"
"github.com/havce/havcebot/ctftime"
"github.com/havce/ctfbot"
"github.com/havce/ctfbot/ctftime"
)

type Server struct {
Expand All @@ -23,7 +23,7 @@ type Server struct {
router handler.Router
client bot.Client

CTFService havcebot.CTFService
CTFService ctfbot.CTFService
CTFTimeClient *ctftime.Client

// Channel default names.
Expand Down
6 changes: 3 additions & 3 deletions discord/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/snowflake/v2"
"github.com/havce/havcebot"
"github.com/havce/ctfbot"
)

func formatTime(t *time.Time) string {
Expand All @@ -17,11 +17,11 @@ func formatTime(t *time.Time) string {
func (s *Server) parentChannel(channelID snowflake.ID) (discord.GuildChannel, error) {
currentChannel, present := s.client.Caches().Channel(channelID)
if !present {
return nil, havcebot.Errorf(havcebot.ENOTFOUND, "Channel not found.")
return nil, ctfbot.Errorf(ctfbot.ENOTFOUND, "Channel not found.")
}
parentChannel, present := s.client.Caches().Channel(*currentChannel.ParentID())
if !present {
return nil, havcebot.Errorf(havcebot.ENOTFOUND, "Parent channel of %s not found.", currentChannel.Name())
return nil, ctfbot.Errorf(ctfbot.ENOTFOUND, "Parent channel of %s not found.", currentChannel.Name())
}

return parentChannel, nil
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version: '3'
services:
havcebot:
ctfbot:
build: .
volumes:
- "./havcebotd.toml:/havcebotd.toml:ro"
- "./ctfbotd.toml:/ctfbotd.toml:ro"
- "database:/database"
restart: unless-stopped

Expand Down
4 changes: 2 additions & 2 deletions error.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package havcebot
package ctfbot

import (
"errors"
Expand Down Expand Up @@ -36,7 +36,7 @@ type Error struct {

// Error implements the error interface. Not used by the application otherwise.
func (e *Error) Error() string {
return fmt.Sprintf("havcebot error: code=%s message=%s", e.Code, e.Message)
return fmt.Sprintf("ctfbot error: code=%s message=%s", e.Code, e.Message)
}

// ErrorCode unwraps an application error and returns its code.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/havce/havcebot
module github.com/havce/ctfbot

go 1.21.6

Expand Down
Loading

0 comments on commit 74a91b8

Please sign in to comment.