PoracleNG is a high-performance Pokemon GO webhook alerting system written in Go. It receives webhooks from a scanner (Golbat), matches them against user-defined tracking rules, and delivers personalised alerts to Discord and Telegram — including bot commands, reconciliation, and all APIs in a single process.
- 📖 Manual / Wiki: https://jfberry.github.io/PoracleNG-docs/
- 💬 Discord: https://discord.gg/AathPCp
Golbat ──webhook──▶ Processor (Go :3030) ──REST API──▶ Discord / Telegram
│
▼
MySQL (read/write)
The processor receives raw webhooks from Golbat, matches them against all user tracking rules in memory, enriches with game data and translations, renders DTS Handlebars templates, and delivers messages directly via the Discord REST API and Telegram Bot API. It also runs the Discord and Telegram bots (commands, reconciliation) and exposes the full /api/* surface consumed by external tools like PoracleWeb.
Migrating from PoracleJS? See Migrating from PoracleJS for an automated migration script and what has changed.
- Go 1.21+
- MySQL 8.0+
- A Golbat instance sending webhooks
Copy the example config and edit it:
cp config/config.example.toml config/config.tomlAt minimum you need to set:
[database]
host = "127.0.0.1"
user = "poracleuser"
password = "poraclepassword"
database = "poracle"
[discord]
enabled = true
token = ["your-bot-token"]
guilds = ["your-guild-id"]
channels = ["registration-channel-id"]
admins = ["your-discord-user-id"]See config/config.example.toml for the full list of settings with documentation.
make buildThis builds the Go processor binary.
With pm2 (recommended for production):
pm2 start ecosystem.config.jsThe included ecosystem.config.js sets kill_timeout: 10000 (10 seconds) to allow graceful shutdown of Discord/Telegram queues. The default pm2 timeout of 1.6 seconds is too short and can leave orphaned processes.
Without pm2:
./start.shThe start script builds the processor binary if needed and runs it.
You can also use make start which builds first then runs start.sh.
On first startup, the processor downloads game data (pokemon names, moves, locales) into resources/, loads all tracking data from the database, and begins accepting webhooks. Rarity and shiny stats will be empty until enough pokemon sightings accumulate.
Configure Golbat to send webhooks to the processor:
http://<your-host>:3030/
A pre-built Docker image is available:
# docker-compose.yml
services:
poracle:
image: ghcr.io/jfberry/poracleng:main
ports:
- "3030:3030"
volumes:
- ./config:/app/config
- ./logs:/app/logs
restart: unless-stoppedCreate config/config.toml (see step 1 above), then:
docker compose up -dThe processor downloads game data on first startup. Logs are written to the logs/ volume.
PoracleNG uses a single TOML config file at config/config.toml.
config/
config.toml # Your configuration (only overrides needed)
config.example.toml # Full reference with all defaults and documentation
geofences/ # Geofence files (GeoJSON or Poracle format)
The processor listens on port 3030 by default:
[processor]
host = "0.0.0.0"
port = 3030See config/config.example.toml for the full list of settings (database, PVP, weather, geofence, discord, telegram, geocoding, tuning, etc.).
By default PoracleNG runs a single Discord bot — the same token sends alerts and processes commands. Setting [discord] command_token splits this into two bots:
- Alerts bot (
[discord] token): handles outbound delivery only — DMs, channel sends, thread sends, edits, TTH-deletes. The token can still be a list if you want multiple delivery bots for rate-limit isolation. - Command bot (
[discord] command_token): handles the gateway side — receives commands, runs reconciliation, manages roles, creates threads.
Why split: cleaner branding (alerts and support can have different bot identities), narrower permission scopes (the alerts bot doesn't need the Server Members / Message Content intents), separate audit-log entries for role changes vs message edits, and resilience to one token being revoked.
Setup steps:
- Create a second bot application in the Discord Developer Portal. Copy its token.
- Enable Server Members Intent and Message Content Intent on the command bot's "Bot" page.
- Invite both bots to every guild PoracleNG operates in. Grant:
- Command bot:
View Channel,Send Messages,Read Message History,Manage Roles(its highest role must outrank any role it manages). - Alerts bot:
Send Messages,Embed Links,Attach Files,Send Messages In Threads,Read Message Historyon each channel it posts to.
- Command bot:
- Add to
config/config.toml:[discord] token = ["alerts-bot-token-here"] command_token = "command-bot-token-here"
- Restart PoracleNG. Logs will show
Discord bot starting in split (using [discord] command_token) mode.
What users will see: two separate DM threads in Discord — alerts arrive from one bot, commands go to the other. Mentioning the command bot in a server channel (@PoracleCommands !poracle) is the way to invoke commands; the alerts bot stays silent except when posting alerts.
Failure modes: if command_token is set but the gateway bot fails to connect (bad token, missing intents), PoracleNG exits at startup rather than silently falling back to single-bot mode. To downgrade back to single-bot operation, remove the command_token line and restart.
Some features use JSON data files (DTS templates, pokemon aliases, etc.). These are loaded from config/ with automatic fallback to bundled defaults:
| File | Purpose | Fallback |
|---|---|---|
dts.json |
Discord/Telegram message templates | Yes (fallbacks/dts.json) |
pokemonAlias.json |
Pokemon name aliases for commands | Yes (fallbacks/pokemonAlias.json) |
partials.json |
Handlebars template partials | Yes (fallbacks/partials.json) |
testdata.json |
Test webhook data for !test command |
Yes (fallbacks/testdata.json) |
geofences/*.json |
Geofence definitions | Yes (fallbacks/geofence.json) |
dts/ |
Additional DTS files (merged with dts.json) | No |
broadcast.json |
Broadcast message templates | No |
channelTemplate.json |
Discord channel auto-creation templates (AUTOCREATE.md) | No |
customMaps/ |
Custom static map definitions | No |
emoji.json |
Custom emoji mappings | No |
custom.<lang>.json |
Custom locale translations | No |
Files with a fallback will use the bundled version from fallbacks/ if you haven't placed a custom version in config/. Files without a fallback are optional features that are simply disabled when absent.
The examples/ directory contains reference copies of all data files plus community-contributed DTS templates to help you get started. To customize a file, copy it from examples/ into config/ and edit it there.
On startup, the processor automatically downloads game data from public GitHub repositories:
- Game Master (pokemon, moves, items, types) from WatWowMap/Masterfile-Generator
- Invasion lineups from WatWowMap/event-info
- Locale translations from WatWowMap/pogo-translations
These are cached in resources/ and reused if the download fails. No manual setup is needed.
PoracleNG/
config/ # Your configuration
config.toml # Your TOML config
config.example.toml # Full reference
geofences/ # Your geofence files
dts.json # Your DTS templates (optional, falls back to default)
... # Other data files (optional)
fallbacks/ # Bundled defaults used when config/ files are missing
examples/ # Reference files and community-contributed templates
dts/ # Example DTS templates from the community
customMaps/ # Example custom map definitions
processor/ # Go processor source
resources/ # Downloaded game data (auto-managed)
logs/ # Log files
scripts/ # Migration and utility scripts
tileserver_templates/ # Tileservercache JSON templates (deployed to your tileserver)
The processor writes logs to the logs/ directory at the project root:
logs/processor.log— main processor log (rotated by size)
Log level and retention are configured in the [logging] section of config.toml:
[logging]
level = "verbose" # silly, debug, verbose, info, warn
max_age = 7 # days to keep log filesPrerequisite: Make sure you are on the latest PoracleJS so that your database schema is up to date before migrating.
An automated migration script converts your existing PoracleJS configuration. Node.js is needed for this one-time script only — the PoracleNG runtime itself is pure Go.
Clone this repo and run:
node scripts/migrate-from-poracle.jsWhen prompted, enter the path to your existing PoracleJS installation.
Run a throwaway node:alpine container that fetches the migration tooling and writes into your mounted config dir:
docker run --rm -it \
-v /path/to/your/poraclejs:/oldporacle \
-v /path/to/your/poracleng-config:/workspace/config \
-w /workspace \
node:20-alpine \
sh -c 'apk add --no-cache git >/dev/null && \
git clone --depth=1 https://github.com/jfberry/PoracleNG /tmp/ng && \
cd /tmp/ng && rm -rf config && ln -s /workspace/config config && \
node scripts/migrate-from-poracle.js'When prompted for Path to your existing PoracleJS installation: enter /oldporacle.
Note: The migrated config files may be owned by
rootdepending on your Docker setup — you may need tochownthem before editing.
- Copies customized data files (DTS, aliases, geofences, etc.) into
config/, skipping any that are unchanged from Poracle's defaults - Converts your
local.jsonoverrides into the unifiedconfig/config.toml, stripping obsolete settings - Prints instructions for the manual steps below
After migrating, review config/config.toml for any incorrect ports or values, ensure your volumes are mounted correctly, and start PoracleNG.
Single process. PoracleNG is a single Go binary — webhook handling, matching, rendering, delivery, bots, and all /api/* endpoints run in one process. External tools like PoracleWeb only need the processor's address.
Webhook destination changes. Golbat must now send webhooks to the processor (default port 3030), not the old alerter port.
Config format. The JS alerter's default.json / local.json system is replaced by a single config/config.toml. All settings use snake_case. See config/config.example.toml for the full reference.
Data files moved. DTS templates, aliases, and other data files are now loaded from config/ (at the project root). Files you haven't customised don't need to be copied — bundled fallbacks in fallbacks/ are used automatically.
Scanner type. MAD scanner support has been removed. The default scanner type is now golbat. If you use RDM, add scanner_type = "rdm" under [database].
Logs directory. The processor writes to logs/ at the project root.
pm2 users. Use the included ecosystem.config.js instead of pm2 start start.sh directly — it sets kill_timeout: 10000 (10s) to allow graceful shutdown of message queues. The default pm2 timeout of 1.6s can leave orphaned processes.
Tileserver templates. The processor generates static map tiles via tileservercache. The templates are in tileserver_templates/ at the repo root — deploy them to your tileserver. Available fields differ slightly from the JS alerter; see TILESERVER.md for the full field reference.
DTS templates. Templates have access to both pre-translated Poracle fields (recommended) and raw webhook fields. Templates can be authored in JSON or TOML (config/dts/*.json or *.toml) and may declare Discord button definitions inline. See DTS.md for the full field reference, the Buttons + TOML sections, and examples/dts/buttons/ for working samples.
Mute commands. !mute <gym|pokemon|area|pokestop|station> <value> [duration:1h] and !unmute ... silence alerts for a time-bounded window without removing the underlying tracking rule. !mute everything self-mutes across all alert types. Per-type aliases work too: !raid mute id:N ≡ !mute raid id:N. Active mutes are shown by !tracked.
Interactive buttons. Operators can attach Discord buttons to alerts via a buttons[] array on any DTS entry. Buttons can dispatch named actions (mute the gym, redeliver to DM) or render ephemeral follow-ups (PVP details, copy coordinates). Buttons require [snapshots] enabled = true in config/config.toml — the opt-in per-delivery snapshot store is what lets click handlers act on the original alert's enriched context. See DTS.md for the schema and docs/buttons-and-snapshots/ for the design.
Operators can manage the processor from within Discord or Telegram using !poracle-admin <group> [subcommand] (alias !pa). This admin-only umbrella covers live reload (dts, geofence, state), a health snapshot (status), alert-limit inspection and reset (ratelimit), summary-buffer management (summary), geocoder cache stats (cache), delivery pause/resume (maintenance), emoji config inspection (emoji), Discord slash-command lifecycle (slash), role reconciliation (reconcile), effective config view (config), and captured WARN/ERROR log inspection (warnings). Full command reference and a smoke-test checklist are in docs/admin-commands/.
All API endpoints are served by the processor (default port 3030). See API.md for the full reference with request/response examples.
| Category | Endpoints | Description |
|---|---|---|
| Webhooks | POST / |
Receive Golbat webhooks |
| State | GET/POST /api/reload, /api/geofence/reload |
Reload tracking rules / geofences |
| Tracking | /api/tracking/{type}/{id} |
CRUD for all 10 tracking types (pokemon, raid, egg, quest, invasion, lure, nest, gym, fort, maxbattle) |
| Humans | /api/humans/* |
User management (areas, location, start/stop, create) |
| Profiles | /api/profiles/* |
Profile CRUD (create, delete, copy, update hours) |
| Geofence | /api/geofence/* |
Geofence data (all, hash, geojson) and tile generation |
| Stats | /api/stats/* |
Rarity, shiny rate, shiny possible |
| Weather | GET /api/weather |
Weather cell data |
| Geocoding | GET /api/geocode/forward |
Forward geocode lookup |
| Messages | POST /api/deliverMessages |
Send confirmation messages to Discord/Telegram |
| Game data | GET /api/masterdata/* |
Pokemon and grunt master data |
| Config | GET /api/config/poracleWeb |
Server config for web UI |
| DTS editor | GET/POST /api/dts/templates, GET /api/dts/actions |
Template CRUD (JSON+TOML round-trip) and button-action discovery |
| Snapshots | GET /api/snapshots/{messageID}?target={id} |
Inspect a delivered alert's enriched view (opt-in) |
| Test | POST /api/test |
Test webhook simulation |
| Health | GET /health, GET /metrics |
Health check and Prometheus metrics |
Two importable Grafana dashboards for the Prometheus metrics exposed by the processor are included:
monitoring/grafana/poracle-operations-lite-dashboard.jsonfor a concise day-to-day operations viewmonitoring/grafana/poracle-observability-dashboard.jsonfor the complete observability view
There is also an example Prometheus scrape config at monitoring/prometheus.yml.example. If you are using Zapdos, add a poracle_processor job to Zapdos/vmagnet/prometheus.yml.
Then:
- Point Prometheus at the processor's
/metricsendpoint. - Add Prometheus as a Grafana data source.
- Import
monitoring/grafana/poracle-operations-lite-dashboard.jsonfor a compact operational view, ormonitoring/grafana/poracle-observability-dashboard.jsonfor the full dashboard.
| From | To | Endpoint | Purpose |
|---|---|---|---|
| Golbat | Processor | POST / |
Raw webhooks (all types) |
| Processor | Discord | Discord REST API v10 + gateway | Alert delivery and bot commands |
| Processor | Telegram | Telegram Bot API + polling | Alert delivery and bot commands |
| PoracleWeb / other | Processor | /api/tracking/*, /api/humans/*, etc. |
External tool integration |
make build # Build the Go processor
make clean # Remove processor binary
make test # Run processor tests
make start # Build + start the processor