Copyright (C) 2020 Chris Northwood
This is a system that allows you to host multi-track unconferences online.
The system provides a virtual Grid (user-generated event schedule) and then links to a Discord server where the actual event runs. The system provides a Discord bot which is mostly responsible for running the schedule and managing the rooms.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.
The app is a Django app, with the core grid being served as a rich React app with communication over GraphQL. Channels is used to support websockets and GraphQL subscriptions for pushing state to the connected clients.
Celery is used to schedule and run timed events (such as doors open, and rooms changing in the schedule) in the background, with Redis as the communication mechanism (for both Celery messages, and Channels's message layers for pushing state changes to connected clients).
The codebase is monolithic, but the architecture is such that it can be scaled horizontally through multiple instances of the app behind a load balancer, with Postgres and Redis holding state, rather than relying on a single process. The intention is for this is to be deployed on some sort of container platform, and the CI server outputs a single Docker image which can be started with multiple invocations to take on the key roles.
You will need to create a Discord server for your event, a role for event moderators to use, and then obtain an OAuth2 secret from Discord.
Clone the repository and create an .env
file at the root of the project
with the following values that we are now going to get:
DISCORD_OAUTH_CLIENT_ID=123456
DISCORD_OAUTH_CLIENT_SECRET=abcdef
DISCORD_OAUTH_BOT_TOKEN=foobarbaz
DISCORD_GUILD_ID=721357132326502400
DISCORD_MODERATOR_ROLE_ID=741674771070320720
DISCORD_WELCOME_CHANNEL_ID=721357132787875932
Register a new bot here. Then:
Go to “OAuth2” on the left menu
- Client ID → DISCORD_OAUTH_CLIENT_ID
- Client secret → DISCORD_OAUTH_CLIENT_SECRET (you’ll need to reset it)
Go to “Bot” in the left menu
- Add a bot
- Give it a snappy username
- Token → DISCORD_OAUTH_BOT_TOKEN (you’ll need to reset it)
In Discord
- Go to
User Settings → Advanced
and enable developer mode - Right click server name and copy ID → DISCORD_GUILD_ID
- Right click the role tag you want for the moderators and copy id → DISCORD_MODERATOR_ROLE_ID
- Right click the welcome channel and copy id → DISCORD_WELCOME_CHANNEL_ID
You've now got all the values you need to proceed.
Now, you can start a local dev environment to test everything works:
- Make sure you have Docker, Poetry and Yarn installed locally
- Run
poetry install
andyarn
in the root to have local copies of the dependencies installed (for your IDE, etc). - Run
docker-compose up
- The webserver is running at http://localhost:8000, and all the background services should be running in Docker with logs appearing in stdout.
- Once you have signed in via Discord, you will only have a regular level of access
- The first admin will need to obtain a shell:
- For the dev environment:
docker exec -it virtualbarcamp_app_1 poetry run ./manage.py shell
- For the live environment:
doctl kubernetes cluster kubeconfig save virtualbarcamp
(to log in to the cluster)
kubectl get pod
and find the name of the pod startingvirtualbarcamp-www
kubectl exec -it virtualbarcamp-www-<pod-full-name> -- ./manage.py shell
- Run this, varying your username appropriately:
from django.contrib.auth.models import User me = User.objects.get(username="laser") me.is_staff = True me.is_superuser = True me.save()
- You will now have access to http://localhost:8000/admin/. You can use the admin screen to give other people staff/superuser access to the admin.
Create a file like this called infrastructure/secrets.tfvars
. Fill in the discord_*
values you already got.
do_token = "..."
gitlab_deploy_token_username = "..."
gitlab_deploy_token_password = "..."
discord_oauth_client_id = "..."
discord_oauth_client_secret = "..."
discord_oauth_bot_token = "..."
discord_guild_id = "..."
discord_moderator_role_id = "..."
discord_welcome_channel_id = "..."
app_hostname = "online.barcampmanchester.co.uk"
letsencrypt_account_email = "you@myemail.com"
- Install Terraform at least version 2.1.2 to avoid certificate errors
- Create a DigitalOcean account. Log in and select 'API' on the left sidebar,
then create a new token. This is
do_token
. - Install doctl and then
doctl auth init
using the DO token you just created. - Add three NS records for the subdomain you want to point to
ns1.digitalocean.com
,ns2.digitalocean.com
andns3.digitalocean.com
- Create a GitLab account if you don't have one already
- Create a new repository with for your bot
- Go to your group -> settings -> general and set visibility level to public
- Go to the repository -> settings -> general and set visibility to public
- Create a token with the read_registry permission. This is token_username and token_password.
- Edit
infrastructure/app.tf#140
with your repository name (TODO: make this a variable) cd terraform
terraform init
(on first run only)terraform apply -var-file secrets.tfvars
and enter the version of the app you wish to deploy, this is the git short hash (e.g.bf1819ac
)- If you have any migrations you need to apply, then you can run these like so:
doctl kubernetes cluster kubeconfig save virtualbarcamp # (to log in to the cluster)
kubectl get pod # and find the name of the pod starting virtualbarcamp-www
kubectl exec virtualbarcamp-www-<pod-full-name> -- /app/init.sh migrate
If you need to scale, you can do so by overriding the default number of workers, or default node size like so:
terraform apply -var-file secrets.tfvars
By default, your virtual BarCamp has 4 states:
- pre-event: people can sign in to the website, but are just shown a holding page with a countdown
- doors open: users can join the Discord server and mingle before the main event
- grid open: the grid is open, and people can place events on it. The event is now basically running
- post-event: a thank you screen is shown
You can schedule any of these event changes to happen at a particular time.
- go to the Django administration page and select "Add" next to "Periodic Tasks"
- Enter a name (e.g., "open doors") and then from "task (registered)" select the event to trigger (open_doors, open_grid, close_event).
- Scroll down to "clocked schedule" and select + to add a time. This time is local time. Click save in the popup.
- Tick "one-off task".
- Save.
This will cause an event to be triggered within a few seconds of the saved time. To stop this from happening, you can select "delete" in the top corner of the periodic task from the list of periodic tasks in the admin.
Sign into the grid using an account which has staff permissions, and then
visit /admin/
. Create the rooms you want to use - this will automatically
create the correct channels in Discord.
Now, in the Django admin, create the sessions you wish to have. If you want a session to be one that people can drop cards into, you must select the rooms you want to be available for those sessions. If you want the session to be a placeholder in the schedule (e.g., to show lunch break, etc), then do not select any rooms and instead complete the "Event" field showing the event which happens at this time.
Access the Django admin pages, go into users, and untick the "active" checkbox. They will no longer be able to log in, and will not be able to sign up again with the same account. They should also be booted from the Discord server if they have joined it.
Sign into the grid using an account which has staf permissions, and then
visit /admin/
. In the Django admin, go into the talks field, find the
talk you want and then delete it.