A Node.js/TypeScript application that monitors Instagram accounts, scrapes the new posts, extracts calendar events using an LLM, geolocates the venues and publishes the results on Nostr following NIP-52 kind 31923 specifications. Controlled via an admin web dashboard.
The typical setup runs in locale, stores data in a Postgres database (either in the same machine or remote), and publishes events to Nostr relays.
I was tired of searching through countless Instagram profiles to find things to do in my city, and Instagram doesn't have an events list feature. So I created this software to aggregate all events in one place. Then I thought that Nostr would be a good place for a shared event calendar.
rhes is an acronym for "rhes is a Helpful Events Server"
Create an empty PostgreSQL database, then:
git clone https://github.com/bitsmonke/rhes.git
cd rhes
cp .env.example .env
# Edit .env — at minimum set DATABASE_URLNOTE: You don't need to init the db with a schema; the app will do it automatically.
Install dependencies:
npm installThen run with:
npm run devOpens the React dev server at http://localhost:5173.
On first run you will be prompted to create an admin account.
1. Create an admin account
On first run, rhes prompts you to create an admin user. For a local installation a simple password is fine.
2. Understand the dashboard
The Dashboard shows which settings come from environment variables (highlighted in green) and which are stored in the database (blue). Database values are managed through the sections in the left menu.
3. Add Instagram profiles
Go to Contents → Profiles → New. Enter the Instagram username and the city where the account organises most of its events. Repeat for each profile you want to monitor.
4. (Optional) Configure image storage
Go to Images Storage and verify the storage path. The default value works for local installations.
5. Set up scraping
Register at apify.com, obtain an API key, and paste it into the Scraping section. Enable the scraper with the toggle. After a few seconds, fetched posts should appear under Contents → Posts.
6. Set up LLM extraction
Register at Google AI Studio, create a Gemini API key, and save it in the LLM Extraction section. Enable extraction with the toggle. The worker reads each post's text and images and extracts event data. If the scraped posts contain events, Contents → Events will start to populate.
7. Set up geolocation
Go to the Geolocation section and set a user agent string (e.g. rhes/1.0 (your@email.com)). Enable geolocation with the toggle. Events will be resolved against OpenStreetMap Nominatim; the result (nominatim_exact_match, nominatim_fuzzy_match, or not_found) appears in the Geo column of the Events table. You can also inspect results in Contents → Events → Geo Filter.
8. Set up Nostr publishing
Create a Nostr account at iris.to (sign up in the left column), then go to Settings → Keys → Copy hex private key and paste it into the Nostr section in rhes. Enable the Nostr worker with the toggle. Published events will show done in the Nostr column. Open any event and click the njump.me button to verify it is live on the network.
You can create alternative copies of .env file, and select one environment when launching the app:
DOTENV_CONFIG_PATH=.env-my-environment npm run dev| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | PostgreSQL connection string |
PORT |
No | Web server port (default: 3000) |
SESSION_SECRET |
Yes in prod | Long random string for session cookies |
The following are optional overrides: if set they take precedence over the values stored in the admin UI:
| Variable | Setting it overrides |
|---|---|
APIFY_API_KEY |
scraper.apify.api_key |
BRIGHTDATA_API_KEY |
scraper.brightdata.api_key |
ANTHROPIC_API_KEY |
llm.claude.api_key |
GOOGLE_API_KEY |
llm.gemini.api_key |
R2_ENDPOINT_URL |
images.r2.endpoint_url |
R2_ACCESS_KEY_ID |
images.r2.access_key_id |
R2_SECRET_ACCESS_KEY |
images.r2.secret_access_key |
R2_BUCKET_NAME |
images.r2.bucket_name |
| Layer | Tech |
|---|---|
| Runtime | Node.js + TypeScript |
| Web server | Express 4 |
| Frontend | React 18 + Vite + Tailwind CSS |
| Database | PostgreSQL |
| LLM | Google Gemini, Claude, Ollama |
| Scraping | Apify, Bright Data |
| Image storage | Local disk, Cloudflare R2 |
| Nostr | nostr-tools |
- Node.js 20+
- PostgreSQL 14+
