Skip to content

Update README file #4

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

Merged
merged 9 commits into from
Jul 15, 2024
Merged
5 changes: 4 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
OP_START_CHANNEL_ID=<OP_START_CHANNEL_ID>
OP_START_CHANNEL_ID=
GUILD_ID=
INTERVIEWER_ROLE_ID=
WELCOME_CHANNEL_ID=
5 changes: 3 additions & 2 deletions .github/workflows/dev-image-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ jobs:
name: Checkout repository
uses: actions/checkout@v4
with:
path: 'owlbot-repo'
path: 'discord-owlbot'
-
name: Build the Docker image
run: docker build . --file owlbot-repo/Dockerfile --tag cntoarma/owlbot:dev
working-directory: discord-owlbot
run: docker build . --file Dockerfile --tag cntoarma/owlbot:dev
-
name: Authenticate to CNTO DockerHub
uses: docker/login-action@v3
Expand Down
7 changes: 4 additions & 3 deletions .github/workflows/stable-image-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ jobs:
name: Checkout repository
uses: actions/checkout@v4
with:
path: 'owlbot-repo'
path: 'discord-owlbot'
-
name: Build the Docker image
run: docker build . --file owlbot-repo/Dockerfile --tag cntoarma/owlbot:latest
working-directory: discord-owlbot
run: docker build . --file Dockerfile --tag cntoarma/owlbot:1.0.0
-
name: Authenticate to CNTO DockerHub
uses: docker/login-action@v3
Expand All @@ -28,4 +29,4 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Publish image to DockerHub
run: docker image push cntoarma/owlbot:latest
run: docker image push cntoarma/owlbot:1.0.0
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
log
*.log
.env.local*
config.henrik-test-server
discord-token.txt

# Byte-compiled / optimized / DLL files
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM alpine:3.19

ADD owlbot-repo /owlbot
ADD . /owlbot
WORKDIR /owlbot

RUN apk add --no-cache python3 py3-pip
Expand Down
62 changes: 38 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,60 @@
# CNTO Owlbot

## What is the Owlbot
## What is Owlbot

Owlbot is CNTO's omnipresent assistant. It is currently implemented as a TeamSpeak user for populating our `stats` pages, and on Discord for automated member pings before our operations begin. R&D has plans to expand the Discord presence to enable staff members to carry out their tasks directly from our Discord, but this is a WIP.
Owlbot is CNTO's omnipresent assistant. It is currently implemented as a TeamSpeak user for populating our `stats` pages, and on Discord for automated member pings before our operations begin. R&D has plans to expand the Discord presence to enable staff members to carry out their tasks directly from our Discord, but this is a WIP. Currently, this repository contains the Discord features of the bot.

## Installation guide
## Owlbot features

Owlbot is available as [a Docker image](https://hub.docker.com/repository/docker/cntoarma/owlbot/general), published on CNTO's DockerHub registry. Two versions are currently maintained: `dev` for development environments and `latest` for production environments.
- Welcome new users joining the Discord server. Upon starting the Docker image, Owlbot connects to the Guild passed as argument and waits for new users to join the guild; whenever a new joiner is detected, Owlbot sends a welcome message pinging the new joiner and providing useful links and references.
- Arma3 operation notification. An optional `operation-notification` command can be passed to `owlbot.py`, in this case Owlbot sends a fire-and-forget type notification to a preset channel pinging community members to remind them to join an upcoming Arma3 operation. The command can be invoked on the host machine (i.e. from outside the Docker container) through `docker exec` or `docker compose exec` to leverage cron jobs.

### 1. Create a Discord application from the Discord Developer Portal
## Running Owlbot

You can follow [this guide](https://discordpy.readthedocs.io/en/stable/discord.html) to create a bot application. CNTO has two versions of the Owlbot managed by R&D: OWL and OWL Dev. The key takeaway from this step is to obtain the bot's secret, used to authenticate the bot against Discord's API.
Owlbot is available as [a Docker image](https://hub.docker.com/repository/docker/cntoarma/owlbot/general), published on CNTO's DockerHub registry. The `cntoarma/owlbot:dev` image is reserved for development builds, for production use the latest tag available based on SemVer (we are working on creating the `cntoarma/owlbot:latest` tag).

### 2. Gather the required parameters
You should run Owlbot through `docker compose`, find an example configuration in the `docker-compose.yml` file.

Within CNTO, the Owlbot is deployed on the `Tools Server`. It requires two parameters to run.
If you are a CNTO Staff member and looking for how R&D deployed this bot, refer to the Forum wiki pages.

1. Discord channel id to post event reminders, this makes use of `.env` file (find a template in the `.env.template` file)
2. Discord bot token to authenticate, this is passed using [docker-compose secrets](https://docs.docker.com/compose/use-secrets/). A plain text file named `discord-token.txt` should be placed in the same directory as `docker-compose.yml`, reference `discord-token.txt.template` for an example.
### Passing Discord authentication token to Owlbot

The `.env` parameters can be passed to Owlbot in two different ways:
Owlbot reads the Discord authentication token from a local file (`/run/secrets/discord_secret`). If using `docker compose`, leverage [the `secrets` section](https://docs.docker.com/compose/use-secrets/). If you are not using docker compose, you will need to [set up the secret](https://docs.docker.com/engine/swarm/secrets/#about-secrets) yourself.

### Required parameters

1. As system environment variables, for example `export OWLBOT_SECRET=1234`
2. As `.env` file in the same directory as the `.env.template`
Separate from the Discord token, a few configuration parameters are required and Owlbot reads them from environment variables. Find an example configuration (suitable for CNTO production environment) in the `config` file. Using docker compose, we pass parameters with the `env_file` command; if you don't use docker compose you will need to pass them one by one upon starting the container.

Owlbot will try to load from a `.env` file, falling back to system variables. If both are present, system variables are used.
```
NOTIFICATION_CHANNEL_ID # Discord channel where members are pinged before operations begin
WELCOME_CHANNEL_ID # Discord channel where newjoiners are linked to for reading rules and regulations
GUILD_ID # Id of the Discord guild (aka Discord server)
INTERVIEWER_ROLE_ID # Role id of interviewing team, used to ping relevant staff whenever a new user joins
```

**Note** if you use the Owlbot Docker image from our public registry, the `.env` file is not yet created by the build process so you need to rely on environment variables. The `.env` file mechanism is supported for local development environments. We do not plan on adding `.env` file support for staging / production environments.
The ids listed above can be found on the Discord server by right clicking on the relevant item (i.e. channel, role). To get the "Copy ID" option, make sure you enable Discord Developer mode on your client.

### 3. Run the Docker image
## Contributing

Start the container through `docker-compose` to ensure proper parameters and secrets loading.
There is not git workflow set in stone, we try to keep things as simple as possible. If you want to contribute to Owlbot: create a new branch from `main`, push your changes to the repository in a feature branch (naming should be along the lines of `feature/a_relevant_name`), open a Pull Request and request a review.
We try to keep Issues updated with a list of tasks, but no guarantees.

```bash
docker-compose up -d
```
### Setting up a local development environment

The use of `virtualenv` or `conda` is recommended to run a Python virtual environment. When working on a new feature or making moderate-to-heavy changes on the codebase, you might want to test the code locally before containerising the application. This requires a local setup of the Discord auth token and environment variables.

- To pass the Discord auth token: we have a testing Discord server, ask R&D Manager for the token. Create a file the Owlbot can read (i.e. `/run/secrets/discord_token`) on your filesystem containing the token.
- To pass the environment variables: there are preconfigured options in the `config` and `config.cnto-test-server` files, to export their values to your environment you can run the following:
```bash
set -o allexport
source CONFIGURATION_FILE
set +o allexport
```

Is the only command you need to get Owlbot up and running.
### Building Docker images

### Updating the crontab file
If developing locally, you'll want to make sure the changes in the application don't break the Docker image so build the image with the `experimental` tag and update `docker-compose.yml` to use said image. Do not push the image tagged as `experimental` to DockerHub.

By default, the Owlbot will send join reminders 10 minutes before our events start (19:35 CET / CEST). This is configured in the `crontab.txt` file that gets installed inside the Owlbot container upon image build. Changing the `crontab.txt` file content and restarting the container or `docker-compose` execution will have no impact on the actual cron entry installed.
This GitHub repository is configured with workflows that automatically build, tag and push the Docker image to CNTO DockerHub registry. The workflows are enabled on the `main` and `dev` branches (workflow specification are contained in the `.github/workflows` directory of this repository). Whenever a new commit is pushed to either branches, workflows are available to start although they require manual approval from the R&D team to proceed.

If you need to change the cron entry for debugging purposes without triggering an image rebuild, get access to a shell within the container (only `/bin/sh` since it's based on an `alpine` image) and edit the crontab manually, for example with `crontab -e`.
If you are releasing a new version of Owlbot, make sure to change the tag in `.github/workflows/stable-image-build.yml` (we are working on automating tag release using git tags).
4 changes: 4 additions & 0 deletions config
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NOTIFICATION_CHANNEL_ID=342739973923143680
WELCOME_CHANNEL_ID=814513168772628540
GUILD_ID=154907081256730624
INTERVIEWER_ROLE_ID=331492014883602433
4 changes: 4 additions & 0 deletions config.cnto-test-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NOTIFICATION_CHANNEL_ID=1101131950745464842
WELCOME_CHANNEL_ID=1101087724007587930
GUILD_ID=1101050893526388758
INTERVIEWER_ROLE_ID=1101061472391536660
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ services:
owlbot:
container_name: owlbot
# restart: unless-stopped
env_file: .env
env_file: ./config
image: cntoarma/owlbot:dev
secrets:
- discord_token
Expand Down
1 change: 0 additions & 1 deletion op-reminder.sh

This file was deleted.

35 changes: 0 additions & 35 deletions op-start-reminder.py

This file was deleted.

62 changes: 62 additions & 0 deletions owlbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
import sys
import discord

# Discord secret and configuration variables retrieval
BOT_SECRET = None
try:
with open('/run/secrets/discord_token', 'r') as f:
BOT_SECRET = f.read()
except FileNotFoundError:
print("No docker secret")
exit(1)

NOTIFICATION_CHANNEL_ID = os.environ['NOTIFICATION_CHANNEL_ID']
WELCOME_CHANNEL_ID= os.environ['WELCOME_CHANNEL_ID']
GUILD_ID = os.environ['GUILD_ID']
INTERVIEWER_ROLE_ID= os.environ['INTERVIEWER_ROLE_ID']

class OwlbotMemberWelcome(discord.Client):
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)

async def on_ready(self):
print('Logged on as', self.user)
guild = await self.fetch_guild(GUILD_ID)

async def on_member_join(self, member):
guild = member.guild
embed = discord.Embed(title="Welcome to CNTO!", color=0xffffff, url="https://cnto-arma.com")
embed.set_author(name="CNTO Server")
embed.set_thumbnail(url="https://forum.cnto-arma.com/uploads/default/original/1X/9e032c33053b34a2bd57d7e90ac03250c7fd3054.png")
embed.add_field(name="", value=f"Hello {member.mention}, Welcome to Carpe Noctem Tactical Operations. Feel free to message an <@&{INTERVIEWER_ROLE_ID}> or take a look at <#{WELCOME_CHANNEL_ID}> if you have any questions! ", inline=True)

# member joined_at function returns datetime object, strf formats output as follows: Day of Week, Number of day, initials of month, year, hour of day in GMT timezone
embed.set_footer(text=f"{member.joined_at.strftime('%a %d %b %Y, %I:%M%p')}")
await guild.system_channel.send(embed = embed)


class OwlbotOperationNotification(discord.Client):
async def on_ready(self):
channel = await self.fetch_channel(NOTIFICATION_CHANNEL_ID)
# TODO: remove embedded role ids and overall message to make it configurable
await channel.send("Tonight's mission will start soon. <@&220093887518081024> and <@&665323023699673108> grab a drink and join us!")
await self.close()

intents = discord.Intents.default()
intents.messages = True
intents.message_content = True
intents.members = True

if __name__ == '__main__':
if len(sys.argv) == 1:
print("Starting member welcome workflow")
client = OwlbotMemberWelcome(intents=intents)
elif len(sys.argv) == 2 and sys.argv[1] == "operation-notification":
print("Starting operation notification workflow")
client = OwlbotOperationNotification(intents=intents)
else:
print("Invalid parameter. Only 'operation-notification' is allowed.")
exit(1)

client.run(BOT_SECRET)
Loading