- Python 71.3%
- Dockerfile 16.3%
- Shell 12.4%
| .dockerignore | chore: pacakge reorg foundation | |
| .env.example | chore: pacakge reorg foundation | |
| .gitignore | chore: pacakge reorg foundation | |
| bot.py | fix: update help link | |
| docker-compose.yml | Add Dockerized seeding and env config | |
| Dockerfile | Add Dockerized seeding and env config | |
| INSTRUCTIONS.md | add instructions | |
| LICENSE | touchdown | |
| mise.toml | chore: pacakge reorg foundation | |
| pois.yaml | Add Dockerized seeding and env config | |
| README.md | Add Dockerized seeding and env config | |
| requirements.txt | chore: pacakge reorg foundation | |
| seed_osm.sh | Add Dockerized seeding and env config | |
Resto-Bot
Code for a Mastodon bot that draws attention to stale POIs.
Resto-bot reads its candidates straight from a GeoParquet file produced by cosmo — no PostgreSQL/PostGIS required. It picks a random POI that hasn't been edited in over five years, toots a link to it, and remembers what it posted in a plain text log.
Data prep
Build the candidate dataset from an OSM PBF extract with cosmo and the
pois.yaml filter. This extracts every amenity=restaurant POI as
a point (building polygons are reduced to their centroid) into one GeoParquet file:
cosmo convert \
--input ~/osm/data/us-latest.osm.pbf \
--filters pois.yaml \
--output ./data/pois.parquet \
--format parquet
Re-run this whenever you want fresher data (e.g. download a new extract and regenerate on a schedule). There is no replication or spatial join step anymore; the place/state shown in each toot is fetched on demand from OSM Nominatim when the bot runs.
Setup
python3 -m venv venv
venv/bin/pip install -r requirements.txt
Configuration is read from environment variables:
DATA_PATH— the GeoParquet file produced by cosmo; defaults to./data/pois.parquetPOSTED_FILE— path to the log of already-posted features; defaults to./posted.txtNOMINATIM_USER_AGENT— identifying string with a contact address; required by Nominatim's usage policyMASTO_BASE_URL,MASTO_ACCESS_TOKEN— your Mastodon instance and token; required unlessDRY_RUN=1DRY_RUN— set to1,true, oryesto print one post without sending it or updatingPOSTED_FILE
Docker
Configuration is read from a .env file in the project root, which compose
auto-loads for the ${VAR} substitutions in docker-compose.yml. Copy the
template and fill it in once — no need to pass settings on the command line:
cp .env.example .env
$EDITOR .env
Build the image:
docker compose build
Seed the data volume from the current Geofabrik United States extract. This downloads
us-latest.osm.pbf and converts it to the GeoParquet file used by the bot:
docker compose --profile seed run --rm seed
The seed step is intentionally separate from the bot run because the US PBF is large.
Override the extract URL or force regeneration. Keep each of these on a single
line — the trailing-\ multi-line form is easy to mangle on paste, which leaves
the variable unset:
OSM_PBF_URL=https://download.geofabrik.de/north-america/us-latest.osm.pbf docker compose --profile seed run --rm seed
FORCE_SEED=1 docker compose --profile seed run --rm seed
FORCE_SEED=1 is required to rebuild pois.parquet after changing pois.yaml,
since seeding skips regeneration when the file already exists.
Run the bot. With DRY_RUN=1 in .env this prints one post without sending it
or touching posted.txt; blank out DRY_RUN and set the Mastodon values for a
real toot. Rebuild first if you've changed bot.py since the last image build:
docker compose build restobot
docker compose run --rm restobot
The restobot-data Docker volume stores the downloaded PBF, generated parquet data,
and posted.txt.
Running
DRY_RUN=1 NOMINATIM_USER_AGENT='restobot/1.0 (+https://codeberg.org/mvexel/restobot)' venv/bin/python3 bot.py
For a real toot, include the Mastodon settings and omit DRY_RUN:
NOMINATIM_USER_AGENT='restobot/1.0 (+https://codeberg.org/mvexel/restobot)' MASTO_BASE_URL='https://botsin.space' MASTO_ACCESS_TOKEN='...' venv/bin/python3 bot.py
Each run selects one stale, not-yet-posted POI, reverse-geocodes its location,
toots about it, and appends its key (e.g. node/123456) to POSTED_FILE.
Crontab it:
NOMINATIM_USER_AGENT='restobot/1.0 (+https://example.org/contact)'
MASTO_BASE_URL='https://botsin.space'
MASTO_ACCESS_TOKEN='...'
0 10 * * * cd /home/mvexel/restobot/ && venv/bin/python3 bot.py >> /home/mvexel/restobot.log