A Discord bot that posts stock and market news from Yahoo Finance and GlobalNewswire to a per-server ticker watchlist.
- Python 3.10+
git clone https://github.com/psalm2517/squawk-bot.git
cd squawk-bot
pip install -r requirements.txt
cp .env.example .envEdit .env and set DISCORD_TOKEN to your bot's token. Optional:
ALERT_USER_ID- your Discord user ID, DMed when a feed enters failure backoff.MAX_TICKERS_PER_SERVER- cap on total tickers tracked per server (leave blank for no cap).
python bot.pyFor production, run under a process supervisor such as systemd. An example unit file is included in the Running as a service section below.
Once the bot is in your Discord server, an admin should run:
/watchlist channel action:set channel:#your-channel
/watchlist ticker action:add ticker:AAPL,MSFT,...
That's the whole setup.
All commands require Manage Server, except the read-only ones (show, recent, status), which anyone can use.
| Command | Description | Cooldown |
|---|---|---|
/watchlist ticker action:<add|remove> ticker:TICKER |
admin - Add/remove tickers (comma-separated). | - |
/watchlist channel action:<set|clear> |
admin - Set the channel news posts to. | - |
/watchlist show |
List tracked tickers. | 60s |
/recent ticker:TICKER |
Fetch the 3 most recent articles for any ticker. | 60s |
/status |
Show bot version, uptime, tickers tracked, last poll, and any feeds in backoff. | 5s |
/blacklist action:<add|remove|show|clear> pattern:text |
admin - Skip articles whose URL contains a given substring (e.g. fool.com). |
- |
Every 2 minutes, Squawk fetches the Yahoo Finance RSS feed for each tracked ticker, plus a GlobeNewswire firehose filtered by company name, and posts any unseen articles. Articles are deduplicated per-server by normalized URL (tracking params stripped). When a ticker is first added, existing articles are marked seen so there's no backlog dump.
Feed failures back off automatically after 3 consecutive errors and alert the server owner (and ALERT_USER_ID if set) via DM. A watchdog force-exits the process if the poll loop hangs, letting systemd restart it.
All state lives in flat JSON files (no database), created automatically and gitignored:
| File | Contents |
|---|---|
watchlist.json |
Per-guild ticker lists |
seen.json |
Per-guild seen article URLs (normalized) |
config.json |
Per-guild news channel |
ticker_names.json |
Cached ticker -> company name alias(es), used to require a headline name match before posting |
blacklist.json |
Per-guild URL substring blocklist |
Example systemd unit file (/etc/systemd/system/squawk.service):
[Unit]
Description=Squawk Discord Bot
After=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/squawk
ExecStart=/opt/squawk/venv/bin/python /opt/squawk/bot.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.targetEnable and manage:
systemctl enable --now squawk
systemctl status squawk
journalctl -u squawk -fSquawk is licensed under the MIT License.