Skip to main content

Almanac Bot

PyPi package Docker Hub

A Twitter bot that tweets historical events (ephemeris) on their anniversary dates. Each day, it finds events that happened on that calendar date (any year) and tweets them with localized text.

Features

  • Tweets historical events on their anniversary (month+day matching)
  • Supports multiple events per day
  • Template variables: ${date} (localized) and ${years_ago} (calculated)
  • Idempotency: won't tweet the same event twice on the same day
  • Dry-run mode for testing without sending tweets
  • Stateless execution triggered by external scheduler (Ofelia)

Dependencies

Quick Start

1. Create configuration

cp config.ini.default config.ini

Fill in the Twitter API credentials and other settings:

[language]
locale=ca_ES  # or en_US, es_ES, etc.

[twitter]
bearer_token=...
consumer_key=...
consumer_secret=...
access_token_key=...
access_token_secret=...

[postgresql]
user=almanac
password=almanac
hostname=postgres
database=almanac

2. Launch with Docker Compose

just docker-up
# or: docker compose up -d

This starts:

  • postgres: PostgreSQL database
  • almanac-bot: The bot container (kept alive for scheduled execution)
  • ofelia: Scheduler that triggers the bot daily at 8:00 AM

3. Load ephemeris data

just docker-load-data
# or: docker exec -it almanac-bot uv run python -m typer almanacbot.data_loader run

CSV format (init_db.csv):

date;text;location
1899-11-29 12:00 Europe/Madrid;El ${date}, avui fa ${years_ago} anys...;(41.38,2.17)

Usage

Manual execution

# Run once (will tweet if there are events for today)
just docker-run

# Dry-run mode (shows what would be tweeted without sending)
just docker-dry-run

Scheduled execution

The bot is automatically triggered by Ofelia at 8:00 AM daily. Check the schedule in compose.yaml:

labels:
  ofelia.job-exec.almanac.schedule: "0 0 8 * * *"  # Daily at 8 AM

View logs

just docker-logs            # Bot logs
just docker-logs-scheduler  # Scheduler logs (follow mode)

Development

Run tests

just test            # Unit tests
just test-cov        # With coverage report
just test-integration  # Integration tests (starts postgres automatically)

Linting

just lint      # Check for issues
just lint-fix  # Auto-fix issues

Build Docker image

just docker-build

Database

Schema

The ephemeris table stores historical events:

CREATE TABLE almanac.ephemeris (
    id serial primary key,
    date timestamp with time zone not null,
    text text not null,
    location point default null,
    last_tweeted_at timestamp with time zone default null
);

Clean database

just docker-clean-db

Just Commands Reference

just help  # Show all available commands
Command Description
just docker-up Start all services
just docker-down Stop all services
just docker-serve Start and follow logs
just docker-load-data Load ephemeris from CSV
just docker-run Run bot manually
just docker-dry-run Run without tweeting
just docker-logs View bot logs
just docker-logs-scheduler View scheduler logs
just docker-build Build Docker image
just docker-clean-db Remove database volume
just test Run unit tests
just test-cov Run tests with coverage
just test-integration Run integration tests
just lint Check code style
just lint-fix Fix code style

Architecture

                    Ofelia (scheduler)
                           │
                           │ triggers daily at 8 AM
                           ▼
┌─────────────────────────────────────────────────────┐
│                   almanac-bot                       │
│                                                     │
│  1. Query ephemeris for today (month+day match)     │
│  2. Filter out already-tweeted (idempotency)        │
│  3. Tweet each event                                │
│  4. Mark as tweeted (last_tweeted_at)               │
│  5. Exit                                            │
└─────────────────────────────────────────────────────┘
                           │
                           ▼
                      PostgreSQL

Production Deployment (Docker Swarm)

For production deployment on Docker Swarm:

1. Create Docker secrets

# Create secret from your config.ini file
docker secret create almanac_config /path/to/config.ini

2. Prepare the stack directory

mkdir -p /path/to/almanac-bot/{init_db,postgres}

# Copy the database init script (creates schema on first run)
cp postgres/init-ephemeris-db.sh /path/to/almanac-bot/postgres/

# Copy your ephemeris CSV data
cp your_data.csv /path/to/almanac-bot/init_db/init_db.csv

3. Configure the stack

# Copy the example and adjust paths
cp docker-compose.swarm.example.yaml /path/to/almanac-bot/docker-compose.swarm.yaml

# Edit the file and update volume paths to match your directory

4. Deploy

docker stack deploy -c /path/to/almanac-bot/docker-compose.swarm.yaml almanac

5. Load ephemeris data into database

After the stack is running, load the CSV data into PostgreSQL:

# Find the bot container
docker ps | grep almanac-bot

# Load the CSV into the database
docker exec <container_id> uv run python -m typer almanacbot.data_loader run

License

MIT

Release files for almanac-bot 1.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for almanac-bot 1.1.3
File Size Uploaded
almanac_bot-1.1.3.tar.gz 9.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for almanac-bot 1.1.3
File Interpreter ABI Platform
almanac_bot-1.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 21.5 kB

Release files / almanac_bot-1.1.3.tar.gz

Download URL almanac_bot-1.1.3.tar.gz
Size 9.3 kB
Tags Source
SHA-256 checksum
How to use checksums
92572ddad2cb24b7dd8527274b5961963ac47629ef6594ea0f23f6e4f3f4a9e3
BLAKE2b-256 checksum
How to use checksums
1dcb276ccd8711e50376566477aa838a8c77860a9403e5183dcca417d6b76ae5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 27, 2025.

Transparency log

Release files / almanac_bot-1.1.3-py3-none-any.whl

Download URL almanac_bot-1.1.3-py3-none-any.whl
Size 12.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
17972c8ca0f0ae78e71bf07a55cbda69300088a603c84e0afb4c37ac4cdb2919
BLAKE2b-256 checksum
How to use checksums
7c3e238337df62ce0c4c90f31320cf219eacd60f544b7f54e86136e9dc305061
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Dec 27, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.3 This release

2 release files

1.0.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page