Skip to content
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

Usage of challenge threads with forumchannel integration #237

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
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
43 changes: 23 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# fiskebot
![](images/fiskebot_logo.png)
# Zookeeper

This bot is still work in process. It is a fork of [fiskebot](https://github.com/dugnadctf/fiskebot/), which is again a fork of [eptbot](https://github.com/ept-team/eptbot), which is again a fork of [igCTF](https://gitlab.com/inequationgroup/igCTF), which is again a fork of [NullCTF](https://github.com/NullPxl/NullCTF).

NB! The bot will only work inside communitychannels due to the use of forum-channels.

Zookeeper feels quite organized in the way that it creates challenge-threads, so the discordservers don't flood over with challengechannels.

Exporting functionality is not avaiable at the moment.

This bot is still work in process. It is a fork of [eptbot](https://github.com/ept-team/eptbot), which is again a fork of [igCTF](https://gitlab.com/inequationgroup/igCTF), which is again a fork of [NullCTF](https://github.com/NullPxl/NullCTF).

## Install

Expand All @@ -26,16 +32,8 @@ The only required variable is `DISCORD_TOKEN`, the rest will use the default val
| `CATEGORY_ARCHIVE_PREFIX` | `archive` | Category to move channels to when the CTF is over. There is a max limit on 50 channels per category. The bot wil automatically move channels to new categories when needed |
| `CHANNEL_EXPORT` | `export` | The channel to upload exports to |
| `CHANNEL_LOGGING_ID` | | If enabled, will send logging to this channel, based on the `LOGGING_DISCORD_LEVEL` logging level |
| `CHANNEL_NAME_DELIMITER` | `-` | The delimiter for the channel names, must be one of `-` or `_`. Results in `-`: `#ctf-challenge-name`, and `_`: `#ctf_challenge_name` |
| `CTFTIME_TEAM_ID` | | CTFtime ID for the `!ctftime team` command |
| `CTFTIME_TEAM_NAME` | | CTFtime name for the `!ctftime team` command |

### start

`docker-compose up --build -d`

### develop

| `CHANNEL_NAME_DELIMITER` | ` ` | The delimiter for the channel names, must be one of `-`, or `_`. Results in `-`: `#ctf-challenge-name`, and `_`: `#ctf_challenge_name` |
| `CTFTIME_TEAM_ID` | | CTFtime ID for the `!ctftime team` command |![enter image description here](images/rumble-add1.PNG)
The `/bot` folder is mounted into the container, so you just need to restart to get your updated changes.
```bash
docker-compose build
Expand Down Expand Up @@ -74,14 +72,17 @@ $ cp git-hook .git/hooks/pre-commit

- `!help` Display the main help commands.

- `!create "ctf name"` This is the command you'll use when you want to begin a new CTF. This command will make a text channel with your supplied name. The bot will also send a message in chat where members can react to join the CTF.
![enter image description here](images/ept-create.PNG)
- `!create "ctf name"` This is the command you'll use when you want to begin a new CTF. This command will make a text channel and a forum-channel with your supplied name. The bot will also send a message in chat where members can react to join the CTF.
![Create CTF-channels](images/rumble-create.PNG)

- `!add <challenge name>` This will create a new thread in the forum-channel for a given challenge.
![Create challengethreads](images/rumble-add1.PNG)
![Challengethreads in forumchannel](images/rumble-add2.PNG)

- `!add <challenge name>` This will create a new channel for a given challenge.
![enter image description here](images/ept-add.PNG)
- `!done [@users ...]` Mark a challenge as done. Needs to be done inside the challenge channel. Optionally specify other users who also contributed to solving the challenge, space separated without the @s.
![enter image description here](images/ept-done.PNG)
- `!ctf archive` Mark the ctf as over and move it to the archive categories (specified in `/bot/config.py`).
- `!done [@users ...]` Mark a challenge as done. Needs to be done inside the challengethread. Optionally specify other users who also contributed to solving the challenge, space separated without the @s.
![Mark challenges as solved](images/rumble-done.PNG)

- `!ctf archive` Mark the ctf as over and move it to the archive categories (specified in `/bot/config.py`).

---

Expand All @@ -102,3 +103,5 @@ $ cp git-hook .git/hooks/pre-commit
![enter image description here](images/ctftime-team.png)

> ### Have a feature request? Make a GitHub issue.

> Please upvote this feature request https://github.com/discord/discord-api-docs/discussions/6084
141 changes: 97 additions & 44 deletions bot/bot.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/usr/bin/env python3
import asyncio
import logging
import os.path
Expand All @@ -15,14 +16,28 @@
from helpers import helpers
from logger import BotLogger

# TODO: Append channel id to naming in db to fix naming conflicts. For both teams and challenges
# Possible naming convention:
# f"{name}-{ctx.channel.id}"

logger = BotLogger("bot")

if not config["token"]:
logger.error("DISCORD_TOKEN has not been set")
exit(1)

client = discord.Client()
bot = commands.Bot(command_prefix=config["prefix"])
#Need to figure out which intents is needed
intents = discord.Intents.all()
intents.members = True
intents.typing = True
intents.presences = True
intents.messages = True
intents.reactions = True

client = discord.Client(intents=intents)
bot = commands.Bot(command_prefix=config["prefix"],intents=intents)



intents = discord.Intents.default()
intents.members = True
Expand Down Expand Up @@ -89,46 +104,69 @@ async def on_raw_reaction_add(payload):
guild = bot.get_guild(payload.guild_id)
chan = bot.get_channel(payload.channel_id)
team = db.teamdb[str(payload.guild_id)].find_one({"msg_id": payload.message_id})
challenge = db.challdb[str(payload.guild_id)].find_one({"msg_id": payload.message_id})
member = await guild.fetch_member(payload.user_id)
if guild and member and chan:
# logger.debug(f"Added reaction: {payload}")
# logger.debug(f"Guild: {guild}, Channel: {chan}, Team: {team}, Member: {member}")
if not team:

if guild and member and chan and not member.bot:
if team:
role = guild.get_role(team["role_id"])
if not role:
logger.error(
f"Not adding role. Could not find role ID {team['role_id']} in Discord"
)
logger.error(team)
return

await member.add_roles(role, reason="User wanted to join team")
# elif challenge and config['react_for_challenge']:
# # logger.debug(f"Adding {member.name} to thread")
# thread = guild.get_thread(challenge['thread_id'])
# await thread.add_user(member)
# db.challdb[str(payload.guild_id)].update_one(
# {"msg_id": payload.message_id}, {"$push": {"working": member.id}}
# )
# logger.debug(f"Added {member.name} to thread")
else:
# logger.error(f"Not adding role. Could find team")
return
role = guild.get_role(team["role_id"])
if not role:
logger.error(
f"Not adding role. Could not find role ID {team['role_id']} in Discord"
)
logger.error(team)
return

await member.add_roles(role, reason="User wanted to join team")
logger.debug(f"Added role {role} to user {member}")


@bot.event
async def on_raw_reaction_remove(payload):
# check if the user is not the bot
guild = bot.get_guild(payload.guild_id)
team = db.teamdb[str(payload.guild_id)].find_one({"msg_id": payload.message_id})
challenge = db.challdb[str(payload.guild_id)].find_one({"msg_id": payload.message_id})
member = await guild.fetch_member(payload.user_id)
if guild and member:

if guild and member and not member.bot:
# logger.debug(f"Removed reaction: {payload}")
# logger.debug(f"Guild: {guild}, Team: {team}, Member: {member}")
if not team:

if team:
role = guild.get_role(team["role_id"])
if not role:
logger.error(f"Not removing role. Could not find role ID {team['role_id']}")
logger.error(team)
return
await member.remove_roles(role, reason="User wanted to leave team")
# logger.debug(f"Removed role {role} from user {member}")

# elif challenge and config['react_for_challenge']:
# # logger.debug(f"Removing {member.name} to thread")
# thread = guild.get_thread(challenge['thread_id'])
# await thread.remove_user(member)
# db.challdb[str(payload.guild_id)].update_one(
# {"msg_id": payload.message_id}, {"$pull": {"working": member.id}}
# )
# logger.debug(f"Removed {member.name} to thread")
else:
# logger.error(f"Not removing role. Could find team")
return
role = guild.get_role(team["role_id"])
if not role:
logger.error(f"Not removing role. Could not find role ID {team['role_id']}")
logger.error(team)
return
await member.remove_roles(role, reason="User wanted to leave team")
logger.debug(f"Removed role {role} from user {member}")





async def embed_help(chan, help_topic, help_text):
emb = discord.Embed(description=help_text, colour=4387968)
emb.set_author(name=help_topic)
Expand All @@ -154,26 +192,31 @@ async def help(ctx, category=None):

@bot.command()
async def request(ctx, feature):
for cid in config["maintainers"]:
creator = bot.get_user(cid)
authors_name = str(ctx.author)
await creator.send(f""":pencil: {authors_name}: {feature}""")
await ctx.send(f""":pencil: Thanks, "{feature}" has been requested!""")
if config["maintainers"]:
for cid in config["maintainers"]:
creator = bot.get_user(cid)
authors_name = str(ctx.author)
await creator.send(f""":pencil: {authors_name}: {feature}""")
await ctx.send(f""":pencil: Thanks, "{feature}" has been requested!""")
else:
await ctx.send(f""":pencil: No maintainers listed in config!""")


@bot.command()
async def report(ctx, error_report):
for cid in config["maintainers"]:
creator = bot.get_user(cid)
authors_name = str(ctx.author)
await creator.send(
f""":triangular_flag_on_post: {authors_name}: {error_report}"""
if config["maintainers"]:
for cid in config["maintainers"]:
creator = bot.get_user(cid)
authors_name = str(ctx.author)
await creator.send(
f""":triangular_flag_on_post: {authors_name}: {error_report}"""
)
await ctx.send(
f""":triangular_flag_on_post: Thanks for the help, "{error_report}" has been reported!"""
)
await ctx.send(
f""":triangular_flag_on_post: Thanks for the help, "{error_report}" has been reported!"""
)


else:
await ctx.send(f""":pencil: No maintainers listed in config!""")

@bot.command()
async def setup(ctx):
if ctx.author.id not in config["maintainers"]:
Expand Down Expand Up @@ -224,7 +267,17 @@ async def exit(ctx):

# -------------------

async def loadExtras(bot):
await bot.load_extension("ctftime")
await bot.load_extension("ctfs")

async def main():

await loadExtras(bot)

async with bot:
await bot.start(config["token"])


if __name__ == "__main__":
bot.load_extension("ctftime")
bot.load_extension("ctfs")
bot.run(config["token"])
asyncio.run(main())
6 changes: 4 additions & 2 deletions bot/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import os


def parse_variable(variable, default=None, valid=None):
value = os.getenv(variable, None)
if default and valid and variable not in valid:
Expand Down Expand Up @@ -76,11 +75,14 @@ def parse_int_list(variable):
},
# The delimiter for the channel names, must be one of "-" or "_". i.e. "-": "#ctf-challenge-name", "_": "#ctf_challenge_name"
"challenge_name_delimiter": parse_variable(
"CHALLENGE_NAME_DELIMITER", "-", valid=["-", "_"]
"CHALLENGE_NAME_DELIMITER", " ", valid=["-", "_", " "]
),
# CTFtime id for the default team to lookup using the `!ctftime team` command
"team": {
"id": parse_variable("CTFTIME_TEAM_ID", -1),
"name": parse_variable("CTFTIME_TEAM_NAME"),
},
# If enabled, users must react to message to make bot add them to thread. Should be visible in threads list anyways.
# Should be True if numbers of participants is high to reduce resourceuse on bot and spam in threads
"react_for_challenge": parse_variable("REACT_FOR_CHALLENGE", False)
}
3 changes: 2 additions & 1 deletion bot/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
SOURCE_FORK1 = "https://github.com/NullPxl/NullCTF"
SOURCE_FORK2 = "https://gitlab.com/inequationgroup/igCTF"
SOURCE_FORK3 = "https://github.com/ept-team/eptbot"
SOURCES_TEXT = f"Source: https://github.com/ekofiskctf/fiskebot\nForked from: {SOURCE_FORK3}\nWho again forked from: {SOURCE_FORK2}\nWho again forked from: {SOURCE_FORK1}"
SOURCE_FORK4 = "https://github.com/ekofiskctf/fiskebot"
SOURCES_TEXT = f"Source: https://github.com/RumbleJungleCTF/RumbleBot\nForked from: {SOURCE_FORK4}\nWho again forked from: {SOURCE_FORK3}\nWho again forked from: {SOURCE_FORK2}\nWho again forked from: {SOURCE_FORK1}"
Loading