Skip to content

Commit

Permalink
Merge main into owlbotv2 branch to backport dockerization work and un…
Browse files Browse the repository at this point in the history
…ify entrypoints
  • Loading branch information
enrico-ghidoni committed Jul 14, 2024
2 parents c9c7792 + 44a7bc4 commit 95bfdb0
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 16 deletions.
3 changes: 1 addition & 2 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
OWLBOT_SECRET=
OP_START_CHANNEL_ID=
GUILD_ID=
INTERVIEWER_ROLE_ID=
WELCOME_CHANNEL_ID=
WELCOME_CHANNEL_ID=
32 changes: 32 additions & 0 deletions .github/workflows/dev-image-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Build Owlbot DEV Docker image

on:
push:
branches: [ "dockerize", "dev" ]

jobs:

build:

runs-on: ubuntu-latest
environment: dev

steps:
-
name: Checkout repository
uses: actions/checkout@v4
with:
path: 'discord-owlbot'
-
name: Build the Docker image
working-directory: discord-owlbot
run: docker build . --file Dockerfile --tag cntoarma/owlbot:dev
-
name: Authenticate to CNTO DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Publish image to DockerHub
run: docker image push cntoarma/owlbot:dev
29 changes: 29 additions & 0 deletions .github/workflows/stable-image-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Build Owlbot Docker image

on:
push:
branches: [ "main" ]

jobs:

build:

runs-on: ubuntu-latest
environment: stable

steps:
-
name: Checkout repository
uses: actions/checkout@v4
-
name: Build the Docker image
run: docker build . --file Dockerfile --tag cntoarma/owlbot:latest
-
name: Authenticate to CNTO DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Publish image to DockerHub
run: docker image push cntoarma/owlbot:latest
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# CNTO Custom
log
*.log
.env.local*
config.henrik-test-server
discord-token.txt

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM alpine:3.19

ADD . /owlbot
WORKDIR /owlbot

RUN apk add --no-cache python3 py3-pip
RUN pip install --break-system-packages -r requirements.txt
RUN crontab crontab.txt

# Install tzdata to switch timezone on deployment
RUN apk add --no-cache tzdata

CMD ["crond", "-f"]
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# CNTO Owlbot

## What is the 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.

## Installation guide

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.

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

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.

### 2. Gather the required parameters

Within CNTO, the Owlbot is deployed on the `Tools Server`. It requires two parameters to run.

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.

The `.env` parameters can be passed to Owlbot in two different ways:


1. As system environment variables, for example `export OWLBOT_SECRET=1234`
2. As `.env` file in the same directory as the `.env.template`

Owlbot will try to load from a `.env` file, falling back to system variables. If both are present, system variables are used.

**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.

### 3. Run the Docker image

Start the container through `docker-compose` to ensure proper parameters and secrets loading.

```bash
docker-compose up -d
```

Is the only command you need to get Owlbot up and running.

### Updating the crontab file

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.

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`.
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
1 change: 1 addition & 0 deletions crontab.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
35 19 * * 2,5 sh /owlbot/op-reminder.sh
1 change: 1 addition & 0 deletions discord-token.txt.template
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DISCORD_TOKEN_HERE
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: "3"
services:
owlbot:
container_name: owlbot
# restart: unless-stopped
env_file: ./config
image: cntoarma/owlbot:dev
secrets:
- discord_token
environment:
- "TZ=Europe/Stockholm"

secrets:
discord_token:
file: ./discord-token.txt
3 changes: 0 additions & 3 deletions op-reminder.sh

This file was deleted.

48 changes: 37 additions & 11 deletions owlbot.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import datetime
import discord
from dotenv import load_dotenv
import os
import sys
import discord

load_dotenv()
# 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)

CHANNEL_ID = os.environ['OP_START_CHANNEL_ID']
BOT_SECRET = os.environ['OWLBOT_SECRET']
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']
WELCOME_CHANNEL_ID= os.environ['WELCOME_CHANNEL_ID']
CNTO_TIMEZONE = datetime.timezone(datetime.timedelta(hours=1),"CNTO_TIMEZONE")
class MyClient(discord.Client):

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

Expand All @@ -30,8 +35,29 @@ async def on_member_join(self, member):
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
client = MyClient(intents=intents)
client.run(BOT_SECRET)

if __name__ == '__main__':
print(BOT_SECRET)
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)

0 comments on commit 95bfdb0

Please sign in to comment.