Click the link below to add the bot to any Discord server you manage:
The bot is hosted and maintained. No setup required on your end — just add it to your server and each member runs
/setupto link their own calendar.
Fetch tasks from Google Calendar or Notion • Daily digests • Reminders • Manual tasks • Nudge teammates
- What Is This Bot?
- Requirements
- Adding the Bot to Discord
- Installation & Configuration
- Hosting on Oracle Cloud VM
- User Setup — Connecting Your Calendar
- Adding Tasks Manually
- Command Reference
- The /nudge Command
- Troubleshooting
- FAQ
The Discord Task Bot connects each member of your Discord server to their own Google Calendar or Notion database. Every morning the bot automatically posts a personalised task digest in the channel of their choice. Members can mark tasks complete, set reminders, add manual tasks, and even nudge each other to stay on track.
Key highlights:
- Per-user calendars — each person links their own source, completely separate from everyone else
- Supports Google Calendar (public iCal) and Notion databases
- Daily digest posted automatically at a configurable time
- Manual tasks — add tasks directly in Discord alongside your calendar events
- Reminders with snooze support
- /nudge — publicly remind a teammate to complete their tasks
- Weekly summary with streak tracking and an upcoming 7-day preview
Before running the bot you need the following on the machine that will host it:
| Requirement | Version | Notes |
|---|---|---|
| Python | 3.10 or later | Installed on the Oracle VM — see Section 5 |
| pip | Latest | Bundled with Python |
| Discord account | Any | To create the bot application |
| Internet access | Always-on | Bot must stay connected 24/7 |
| Google or Notion account | Any | At least one user needs a calendar |
Python packages (installed automatically from requirements.txt):
discord.py >= 2.3— Discord bot frameworkhttpx— async HTTP client for calendar fetchingicalendar— parses Google Calendar iCal feedspython-dotenv— loads.envconfig filepytz— timezone support
Optional: Docker — You can run the bot inside a Docker container on your Oracle VM instead of installing Python directly. A
Dockerfileanddocker-compose.ymlare included. See Option B in Section 5.
These steps are done once by the server owner or administrator.
- Go to discord.com/developers/applications and log in.
- Click "New Application" in the top-right corner.
- Give it a name (e.g.
Task Bot) and click Create. - Go to the "Bot" tab in the left sidebar.
- Click "Add Bot" and confirm.
- Under "Privileged Gateway Intents", enable both:
- Server Members Intent
- Message Content Intent
- Click "Save Changes".
- Click "Reset Token" to reveal your bot token. Copy it — you will need it later.
Keep your token secret. Never share your bot token publicly. Anyone with it can control your bot. If it leaks, regenerate it immediately from the Developer Portal.
Build the invite URL manually. Replace YOUR_CLIENT_ID with your application's Client ID (found on the "General Information" page of your app in the Developer Portal):
https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=84992&scope=bot+applications.commands
How to find your Client ID:
- In the Developer Portal, click your application.
- Click "General Information" in the left sidebar.
- Copy the "Application ID" — this is your Client ID.
Paste the completed URL into your browser, select your server from the dropdown, and click Authorise.
The permissions=84992 covers exactly what the bot needs:
| Permission | Why it's needed |
|---|---|
View Channels |
Lets the bot see the channels it posts into |
Send Messages |
Posts daily digests and reminder alerts |
Embed Links |
Sends formatted embed cards for task lists |
Read Message History |
Allows the bot to function correctly in channels |
The bot will now appear in your server's member list but will show as offline until you run it.
Note on the "Redirects" field: The Developer Portal may show a Redirects section with the note "You must specify at least one URI for authentication to work." This only applies to OAuth2 user login flows (e.g. "Sign in with Discord" on a website). This bot uses the
botscope to join your server — it does not perform user authentication and does not need a redirect URI. Leave the field empty.
discord_task_bot/
├── src/
│ ├── bot.py # Entry point — all slash commands
│ ├── task_manager.py # Digest and reminder logic
│ ├── calendar_fetcher.py # Google Calendar and Notion fetchers
│ ├── database.py # SQLite storage (per-user isolation)
│ └── config.py # Reads environment variables
├── Dockerfile # Docker image definition
├── docker-compose.yml # Docker Compose support
├── requirements.txt # Python dependencies
└── .env.example # Configuration template — copy to .env
A template is included. Copy it and fill in your values:
cp .env.example .envThen open .env in any editor and set your values:
# Required
DISCORD_BOT_TOKEN=your_bot_token_here
# Optional — defaults shown
DAILY_POST_HOUR=8
DAILY_POST_MINUTE=0
TIMEZONE=America/Toronto
DATABASE_PATH=taskbot.db| Variable | Default | Description |
|---|---|---|
DISCORD_BOT_TOKEN |
(required) | Bot token from the Developer Portal |
DAILY_POST_HOUR |
8 |
Hour to post daily digests (24-hour, 0–23) |
DAILY_POST_MINUTE |
0 |
Minute to post (0–59) |
TIMEZONE |
America/Toronto |
Your server's timezone (full list) |
DATABASE_PATH |
taskbot.db |
Path to the SQLite file (created automatically) |
The bot will refuse to start with a clear error message if
DISCORD_BOT_TOKENis missing or empty, or if the hour/minute values are out of range.
# Create and activate a virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txtpython src/bot.pyYou should see:
INFO TaskBot: Logged in as Task Bot#1234 (ID: 123456789)
INFO TaskBot: Slash commands synced.
INFO TaskBot: Daily digest scheduled for 08:00 America/Toronto
Slash commands may take 1–5 minutes to appear in Discord after the first sync. Press
Ctrl+Cto stop — Section 5 covers keeping it running permanently on Oracle VM.
Oracle Cloud's Always Free tier includes two Arm-based VMs (4 OCPUs and 24 GB RAM combined) at no cost, permanently. This is more than enough to run the bot indefinitely.
Two deployment options are covered below. Option A (systemd) is simpler to manage day-to-day. Option B (Docker) is useful if you prefer container isolation or already run Docker on your VM.
systemd runs the bot as a background service, starts it automatically on VM boot, and restarts it immediately if it crashes.
ssh -i /path/to/your_private_key ubuntu@<your-vm-public-ip>Your VM's public IP is shown in the OCI Console under Compute → Instances → your instance → Instance information.
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3 python3-pip python3-venvVerify:
python3 --version # should be 3.10 or laterFrom your local machine, copy the project folder to the VM:
scp -i /path/to/your_private_key -r /path/to/discord_task_bot ubuntu@<ip>:~/Or, if the project is in a git repository, clone it directly on the VM:
git clone <your-repo-url> ~/discord_task_botcd ~/discord_task_bot
cp .env.example .env
nano .envFill in your values and save with Ctrl+O, Enter, Ctrl+X.
Set the database path to keep it inside the project folder:
DISCORD_BOT_TOKEN=your_token_here
DAILY_POST_HOUR=8
DAILY_POST_MINUTE=0
TIMEZONE=America/Toronto
DATABASE_PATH=/home/ubuntu/discord_task_bot/taskbot.dbcd ~/discord_task_bot
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtpython src/bot.pyConfirm the bot logs in and the slash commands sync, then press Ctrl+C.
sudo nano /etc/systemd/system/taskbot.servicePaste the following exactly — adjust the paths only if you installed in a different directory:
[Unit]
Description=Discord Task Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/discord_task_bot
EnvironmentFile=/home/ubuntu/discord_task_bot/.env
ExecStart=/home/ubuntu/discord_task_bot/venv/bin/python src/bot.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetSave and close (Ctrl+O, Enter, Ctrl+X).
sudo systemctl daemon-reload
sudo systemctl enable taskbot # start automatically on boot
sudo systemctl start taskbot # start right nowCheck it's running:
sudo systemctl status taskbotYou should see Active: active (running).
sudo systemctl status taskbot # Is it running?
sudo systemctl restart taskbot # Restart after changes
sudo systemctl stop taskbot # Stop the bot
sudo systemctl disable taskbot # Prevent starting on boot
journalctl -u taskbot -f # Live log stream
journalctl -u taskbot -n 100 # Last 100 log linesFrom your local machine, upload the changed file(s):
scp -i /path/to/key src/bot.py ubuntu@<ip>:~/discord_task_bot/src/Then restart the service on the VM:
sudo systemctl restart taskbot
journalctl -u taskbot -f # confirm it came back up cleanlyUse this if you prefer Docker. The database is stored on a named Docker volume so it survives container restarts and rebuilds.
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker ubuntuLog out and back in for the group change to take effect:
exit
ssh -i /path/to/key ubuntu@<ip># From your local machine
scp -i /path/to/key -r /path/to/discord_task_bot ubuntu@<ip>:~/Then on the VM:
cd ~/discord_task_bot
nano .envUse /data/taskbot.db as the database path — this is where Docker mounts the persistent volume:
DISCORD_BOT_TOKEN=your_token_here
DAILY_POST_HOUR=8
DAILY_POST_MINUTE=0
TIMEZONE=America/Toronto
DATABASE_PATH=/data/taskbot.dbdocker compose up -d # build image and start in background
docker compose logs -f # stream logsDocker Compose is configured with restart: unless-stopped, so the bot restarts automatically on crash or VM reboot.
docker compose logs -f # Live log stream
docker compose ps # Is the container running?
docker compose restart # Restart the bot
docker compose down # Stop and remove the container
docker compose up -d --build # Rebuild image after code changes# From local machine — upload changed files
scp -i /path/to/key src/bot.py ubuntu@<ip>:~/discord_task_bot/src/
# On the VM — rebuild and restart
cd ~/discord_task_bot
docker compose up -d --buildpython3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python src/bot.pyThe bot goes offline when you close the terminal or shut down your computer. Use Option A or B on the Oracle VM for permanent hosting.
Every user in the server sets up independently. Their data is completely private — no one else can see their task list or calendar ID.
Per-user isolation: Each user has their own row in the database. Tasks, completions, reminders, and settings are strictly scoped to the individual Discord user ID. Other members — including server admins — cannot view your tasks.
Every user must run /setup before using any other command, including manual tasks. Setup takes about 30 seconds.
- Open Google Calendar at calendar.google.com.
- In the left sidebar, hover over the calendar you want to use and click the three-dot menu (⋮).
- Select "Settings and sharing".
- Under "Access permissions for events", tick "Make available to public". Click OK on the warning.
- Scroll down to "Integrate calendar". Copy the Calendar ID — it looks like:
or for a shared/secondary calendar:
yourname@gmail.comabc123xyz@group.calendar.google.com - In Discord, run:
/setup source:Google Calendar calendar_id:<paste your ID here> channel:#your-channel
What gets fetched? The bot reads events from your Google Calendar's public iCal feed. Google Tasks are not included — only Calendar Events appear. The calendar must remain public for the bot to access it.
Notion requires two things: an integration token and a database ID.
- Go to notion.so/my-integrations and click "+ New integration".
- Name it (e.g.
Discord Task Bot), select your workspace, and click Submit. - Copy the Internal Integration Token — it starts with
secret_. - Set the capabilities to Read content only — the bot never writes to Notion.
- Open the Notion database you want to use.
- Click "..." in the top-right corner → "Connections" → find and add your integration.
- Copy the Database ID from the URL bar. The URL looks like:
The 32-character string between the last
https://notion.so/yourworkspace/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX?v=.../and the?is your Database ID.
Your Notion database must have at least one of these property names for date filtering to work:
| Property Name | Type |
|---|---|
Date |
Date |
Due |
Date |
Due Date |
Date |
Deadline |
Date |
The bot also looks for a Description, Notes, or Body rich-text property to show task descriptions.
/setup source:Notion calendar_id:<database-id> channel:#your-channel notion_token:<secret_...>
You don't need a calendar event for every task. Use /add to create tasks directly in Discord. Manual tasks are stored in the bot's database and appear alongside your calendar events in /tasks.
Note: You still need to run
/setupfirst (to register your digest channel), even if you only plan to use manual tasks.
/add name:Buy groceries due:2024-06-15 description:Milk, eggs, bread
All parameters except name are optional. If you omit due, the task has no due date and appears in every daily digest until you complete it.
/mytasks
Shows all pending manual tasks. Add show_done:True to include completed ones.
/complete task_name:Buy groceries
Partial matches work — typing groceries is enough if it uniquely identifies the task. The bot checks manual tasks first, then calendar events.
/delete task_name:Buy groceries
Permanently removes the task from the database.
All commands are Discord slash commands. Type
/in the message box to see them. Responses marked (private) are ephemeral — only visible to you.
| Command | Description |
|---|---|
/setup |
Link your Google Calendar or Notion database. Choose the source from the dropdown. (private) |
/status |
Show your current configuration, streak, and stats. (private) |
/unlink |
Remove all your data from the bot. (private) |
| Command | Description |
|---|---|
/tasks [date] |
Show your tasks for today or a specific date (YYYY-MM-DD). (private) |
/mytasks [show_done] |
List all your manual (non-calendar) tasks. (private) |
/weekly |
Weekly summary with per-day chart, streak, and upcoming tasks. (private) |
| Command | Description |
|---|---|
/complete <task> |
Mark a task as complete — works for both calendar and manual tasks. (private) |
/add <name> [due] [description] |
Add a manual task directly in Discord. (private) |
/delete <task> |
Delete a manual task. (private) |
| Command | Description |
|---|---|
/reminder <task> <datetime> |
Set a reminder. Format: YYYY-MM-DD HH:MM (24-hour). (private) |
/reminders |
List all your pending reminders. (private) |
/snooze <task> [minutes] |
Snooze a reminder by N minutes (default: 30). (private) |
/cancelreminder <task> |
Cancel a pending reminder. (private) |
| Command | Description |
|---|---|
/nudge @member [message] |
Publicly remind another member to complete their tasks. Visible to the whole channel. |
/help |
Show the full command list. (private) |
The /nudge command lets any server member publicly remind another member to check off their tasks. It posts a visible message in the current channel tagging the target user.
/nudge member:@Jane message:Don't forget your standup tasks!
The nudge embed shows:
- Who sent the nudge
- The optional custom message (or a friendly default if none is provided)
- How many tasks are still pending for the nudged user today (count only — task names stay private)
- Quick-action reminders (
/tasks,/complete,/weekly)
Privacy note: The nudge shows only a task count — never the actual task names. A teammate can see that you have 3 tasks pending but not what those tasks are.
Rules:
- You cannot nudge yourself — use
/reminderinstead. - You cannot nudge bots.
- If the target user has not run
/setup, you are told privately rather than posting publicly.
| Problem | Likely Cause | Fix |
|---|---|---|
| Bot won't start: "DISCORD_BOT_TOKEN is not set" | Missing .env file or empty token |
Create .env in the project root and set DISCORD_BOT_TOKEN |
| Bot won't start: hour/minute out of range | Invalid schedule values in .env |
Set DAILY_POST_HOUR to 0–23 and DAILY_POST_MINUTE to 0–59 |
| Commands don't appear in Discord | Slash commands haven't propagated yet | Wait 5–10 minutes after starting the bot for the first time |
| systemd service won't start | Wrong paths in service file | Run journalctl -u taskbot -n 50 and check the error; verify all paths in the [Service] section |
| Google Calendar returns no tasks | Calendar is not set to public | Go to Calendar Settings → Access Permissions → Make available to public |
| Google Calendar returns wrong tasks | Wrong Calendar ID | Confirm the ID in Google Calendar → Settings and sharing → Integrate calendar |
Notion returns 401 Unauthorized |
Invalid token or integration not connected | Check the token starts with secret_ and the integration is added to the database via Connections |
Notion returns 404 Not Found |
Wrong Database ID | Copy the 32-character ID from the Notion page URL — not a page inside the database |
| Notion tasks not showing for a date | Date property name not recognised | Rename your date property to one of: Date, Due, Due Date, or Deadline |
| Daily digest not posting | Bot is offline or wrong channel | Run sudo systemctl status taskbot and check the channel in /status is accessible |
| Reminders not firing | Bot was offline at reminder time | Keep the bot running 24/7 — overdue reminders fire on next startup |
| Can't see bot commands | Missing applications.commands scope |
Re-invite the bot using the OAuth2 URL in Section 3 |
/setup says "Could not connect" |
Calendar not public / bad Notion token | Follow the setup steps in Section 6 exactly |
Can multiple people use the bot in the same server?
Yes — that is the core design. Each user who runs /setup gets their own independent task list, reminder schedule, and digest channel. There is no limit on the number of users per server.
Can two users share the same digest channel?
Yes. The bot posts separate messages, each mentioning the respective user. You can also give everyone their own private channel for a cleaner experience.
Does my Google Calendar have to stay public?
Yes, for as long as you want the bot to fetch your tasks. If you make it private, the bot will show a connection error in your next digest. You can re-link at any time with /setup.
Are my Notion tokens stored securely?
Tokens are stored in the local SQLite database on the Oracle VM. Use a dedicated integration token with read-only access. The bot never writes to or modifies your Notion data.
What happens if I run /setup again?
Your configuration is updated in place. Your existing completions, reminders, and manual tasks are preserved. Only your calendar source, ID, channel, and token are updated.
How do I change my digest channel?
Run /setup again with the new channel. All other settings remain unchanged.
Will the bot post digests on days when I have no tasks?
Yes — it posts a message confirming there are no tasks scheduled for that day, so you always know the bot is working.
Can I use both Google Calendar events and manual tasks at the same time?
Yes. Manual tasks (added with /add) appear in /tasks alongside your Google Calendar or Notion events. The /complete command checks manual tasks first, then calendar events.
I only want manual tasks — do I still need a Google Calendar or Notion account?
You still need to run /setup (which requires a calendar source) to register your digest channel. If you don't have a calendar, set up a free Google account, create a calendar, make it public, and use that Calendar ID — you can leave it empty and use only manual tasks.
Discord Task Bot — built with discord.py, httpx, and icalendar.