{"id":14069293,"url":"https://github.com/quambene/pigeon-rs","last_synced_at":"2025-04-04T20:07:44.025Z","repository":{"id":44326328,"uuid":"423860865","full_name":"quambene/pigeon-rs","owner":"quambene","description":"Open source email automation written in Rust","archived":false,"fork":false,"pushed_at":"2024-12-29T02:35:50.000Z","size":324,"stargazers_count":80,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-28T19:08:00.987Z","etag":null,"topics":["cli","command-line-tool","csv","database","db","email","email-automation","email-marketing","email-templates","postgres","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/quambene.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-02T13:45:59.000Z","updated_at":"2025-03-14T20:29:13.000Z","dependencies_parsed_at":"2024-04-19T22:34:06.435Z","dependency_job_id":"98796ab4-f7d3-4bd7-b3d2-2dc66d4b3bf2","html_url":"https://github.com/quambene/pigeon-rs","commit_stats":{"total_commits":200,"total_committers":1,"mean_commits":200.0,"dds":0.0,"last_synced_commit":"7752727fc1a8f062a541bffebb4fe5a7abe9ee3a"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quambene%2Fpigeon-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quambene%2Fpigeon-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quambene%2Fpigeon-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/quambene%2Fpigeon-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/quambene","download_url":"https://codeload.github.com/quambene/pigeon-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242671,"owners_count":20907133,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","command-line-tool","csv","database","db","email","email-automation","email-marketing","email-templates","postgres","rust"],"created_at":"2024-08-13T07:06:49.063Z","updated_at":"2025-04-04T20:07:44.010Z","avatar_url":"https://github.com/quambene.png","language":"Rust","funding_links":[],"categories":["cli","Rust"],"sub_categories":[],"readme":"\u003c!-- markdownlint-disable MD033 --\u003e\n\n# Pigeon\n\n[![latest version](https://img.shields.io/crates/v/pigeon-rs.svg)](https://crates.io/crates/pigeon-rs)\n[![documentation](https://docs.rs/pigeon-rs/badge.svg)](https://docs.rs/pigeon-rs/)\n[![build status](https://github.com/quambene/pigeon-rs/actions/workflows/rust-ci.yml/badge.svg)](https://github.com/quambene/pigeon-rs/actions/workflows/rust-ci.yml)\n[![codecov](https://codecov.io/gh/quambene/pigeon-rs/graph/badge.svg)](https://app.codecov.io/gh/quambene/pigeon-rs)\n[![dependency status](https://deps.rs/repo/github/quambene/pigeon-rs/status.svg)](https://deps.rs/repo/github/quambene/pigeon-rs)\n\nPigeon is a command line tool for automating your email workflow in a cheap and efficient way. Utilize your most efficient dev tools you are already familiar with.\n\nFor example, query the subscribers of your newsletter, create a plaintext and html email from a template file, and send it to all of them:\n\n``` bash\npigeon send-bulk \\\n    sender@your-domain.com \\\n    --receiver-query \"select email from user where newsletter_confirmed = true\" \\\n    --message-file \"message.yaml\" \\\n    --display \\\n    --assume-yes\n```\n\n``` console\n\u003e Display query result: shape: (4, 1)\n+------------------------------+\n| email                        |\n| ---                          |\n| str                          |\n+==============================+\n| \"marie@curie.com\"            |\n+------------------------------+\n| \"alexandre@grothendieck.com\" |\n+------------------------------+\n| \"emmy@noether.com\"           |\n+------------------------------+\n| \"elie@cartan.com\"            |\n+------------------------------+\n\u003e Sending email to 4 receivers ...\nmarie@curie.com ... ok\nalexandre@grothendieck.com ... ok\nemmy@noether.com ... ok\nelie@cartan.com ... ok\n```\n\n- [Install Pigeon](#install-pigeon)\n  - [Install Pigeon from crates.io](#install-pigeon-from-cratesio)\n  - [Install Pigeon from github.com](#install-pigeon-from-githubcom)\n- [Getting help](#getting-help)\n- [Usage](#usage)\n  - [Send email to a single receiver](#send-email-to-a-single-receiver)\n  - [Send bulk email to multiple receivers](#send-bulk-email-to-multiple-receivers)\n  - [Personalize your emails](#personalize-your-emails)\n- [How to connect](#how-to-connect)\n  - [How to connect to SMTP server](#how-to-connect-to-smtp-server)\n  - [How to connect to email provider API](#how-to-connect-to-email-provider-api)\n  - [How to connect to postgres database](#how-to-connect-to-postgres-database)\n- [Integrations](#integrations)\n  - [Email protocols](#email-protocols)\n  - [Third-party APIs](#third-party-apis)\n  - [Data sources](#data-sources)\n- [Comparison with Mailchimp, Sendgrid, and ConvertKit](#comparison-with-mailchimp-sendgrid-and-convertkit)\n- [Testing](#testing)\n\n## Install Pigeon\n\n### Install Pigeon from [crates.io](https://crates.io/crates/pigeon-rs)\n\n``` bash\n# Build and install pigeon binary to ~/.cargo/bin\ncargo install pigeon-rs\n```\n\n_Note:_ Run `cargo install pigeon-rs` again to update to the latest version. Uninstall the pigeon binary with `cargo uninstall pigeon-rs`.\n\n### Install Pigeon from [github.com](https://github.com/quambene/pigeon-rs)\n\n``` bash\n# Clone repository\ngit clone git@github.com:quambene/pigeon-rs.git\ncd pigeon-rs\n\n# Build and install pigeon binary to ~/.cargo/bin\ncargo install --path .\n```\n\n_Note:_ Add `$HOME/.cargo/bin` to your `PATH` if it is missing:\n\n``` bash\nexport PATH=\"$HOME/.cargo/bin:$PATH\"\n```\n\n## Getting help\n\nFor getting help, try one of the following:\n\n``` bash\n# Check version\npigeon --version\n\n# Print help\npigeon --help\n\n# Print help for subcommand\npigeon help send\npigeon help send-bulk\npigeon help connect\npigeon help init\npigeon help query\npigeon help simple-query\npigeon help read\n```\n\n## Usage\n\nCheck connection to your SMTP server with `pigeon connect`:\n\n``` bash\npigeon connect\n```\n\n\u003e Connecting to SMTP server 'email-smtp.eu-west-1.amazonaws.com' ... \u003cspan style=\"color:MediumSeaGreen\"\u003eok\u003c/span\u003e\n\nSee currently supported [integrations](#integrations) and [how to connect](#how-to-connect) below.\n\n_Note:_ You can also check connection to third-party APIs instead of using the SMTP protocol. For example, using AWS Simple Email Service (SES): `pigeon connect aws`.\n\n### Send email to a single receiver\n\nSend a single email with subject and content:\n\n``` bash\npigeon send \\\n    sender@your-domain.com \\\n    receiver@gmail.com \\\n    --subject \"Test subject\" \\\n    --content \"This is a test email.\"\n```\n\nSend a single email with message defined in separate template file:\n\n``` bash\npigeon send \\\n    sender@your-domain.com \\\n    receiver@gmail.com \\\n    --message-file \"message.yaml\"\n```\n\nThe message template `message.yaml` is created with subcommand `init`:\n\n``` bash\npigeon init\n```\n\n_Note:_ One of the advantages of a `--message-file` is that you can also draft the html version of your email. In contrast, with the options `--subject` and `--content` the email will only be sent in plaintext format.\n\nIf you prefer a dedicated HTML file for drafting your email, use the following command:\n\n``` bash\npigeon send \\\n    sender@your-domain.com \\\n    receiver@gmail.com \\\n    --subject \"Test subject\" \\\n    --text-file \"./message.txt\" \\\n    --html-file \"./message.html\"\n```\n\nwhere `--text-file` defines the plaintext and `--html-file` the HTML version of your email.\n\n### Send bulk email to multiple receivers\n\nFor example, query relevant users which confirmed to receive your newsletter, and send an email to all of them.\n\nLet's check the query first via `pigeon query`:\n\n``` bash\npigeon query --display \"select email from user where newsletter_confirmed = true\"\n```\n\n``` console\n\u003e Display query result: shape: (4, 1)\n+------------------------------+\n| email                        |\n| ---                          |\n| str                          |\n+==============================+\n| \"marie@curie.com\"            |\n+------------------------------+\n| \"alexandre@grothendieck.com\" |\n+------------------------------+\n| \"emmy@noether.com\"           |\n+------------------------------+\n| \"elie@cartan.com\"            |\n+------------------------------+\n```\n\nSee [how to connect](#how-to-connect) below to connect your database.\n\n_Note:_ You can also `--save` your query as a csv file: `pigeon query --save \u003cmy-query\u003e`.\n\nNow send your newsletter to the queried receivers. If the table column name is different to \"email\" use `--receiver-column` to define a different column name. Let's try a `--dry-run` without confirmation `--assume-yes` first:\n\n``` bash\npigeon send-bulk \\\n    albert@einstein.com \\\n    --receiver-query \"select email from user where newsletter_confirmed = true\" \\\n    --message-file \"message.yaml\" \\\n    --assume-yes \\\n    --dry-run\n```\n\n``` console\n\u003e Sending email to 4 receivers ...\nmarie@curie.com ... dry run\nalexandre@grothendieck.com ... dry run\nemmy@noether.com ... dry run\nelie@cartan.com ... dry run\n```\n\nAfter double checking, you can submit the same command without `--dry-run`. Remove `--assume-yes` as well for explicit confirmation.\n\n_Note:_ You can also send a bulk email to email adresses defined in a csv file instead of a query result. In this case, use option `--receiver-file` instead of `--receiver-query`. You can check the contents of a csv file via subcommand `read`, e.g. `pigeon read recipients.csv`.\n\n### Personalize your emails\n\nIf you need more individual emails, you can _personalize_ your emails with option `--personalize`. Again, let's start by checking the relevant query:\n\n``` bash\npigeon query --display \"select first_name, last_name, email from user where newsletter_confirmed = true\"\n```\n\n``` console\n\u003e Display query result: shape: (4, 3)\n+-------------+----------------+------------------------------+\n| first_name  | last_name      | email                        |\n| ---         | ---            | ---                          |\n| str         | str            | str                          |\n+=============+================+==============================+\n| \"Marie\"     | \"Curie\"        | \"marie@curie.com\"            |\n+-------------+----------------+------------------------------+\n| \"Alexandre\" | \"Grothendieck\" | \"alexandre@grothendieck.com\" |\n+-------------+----------------+------------------------------+\n| \"Emmy\"      | \"Noether\"      | \"emmy@noether.com\"           |\n+-------------+----------------+------------------------------+\n| \"Elie\"      | \"Cartan\"       | \"elie@cartan.com\"            |\n+-------------+----------------+------------------------------+\n```\n\nIn your message template `message.yaml` use variables in curly brackets, like `{first_name}` and `{last_name}`. Then define personalized colums as parameters for option `--personalize`. Finally, let's display everything with `--display`:\n\n``` bash\npigeon send-bulk \\\n    albert@einstein.com \\\n    --receiver-query \"select first_name, last_name, email from user where newsletter_confirmed = true\" \\\n    --message-file \"message.yaml\" \\\n    --personalize \"first_name\" \"last_name\" \\\n    --display\n```\n\n``` console\n\u003e Display message file: MessageTemplate {\n    message: Message {\n        subject: \"Issue No. 1\",\n        text: \"Dear {first_name} {last_name},\n            Welcome to my newsletter. We are doing hard sciences here.\n            Sincerely, Albert Einstein\",\n        html: \"Dear {first_name} {last_name},\n            Welcome to my newsletter. We are doing hard sciences here.\n            Sincerely, Albert Einstein\",\n    },\n}\n\u003e Display emails: BulkEmail {\n    emails: [\n        Email {\n            sender: \"albert@einstein.com\",\n            receiver: \"marie@curie.com\",\n            message: Message {\n                subject: \"Issue No. 1\",\n                text: \"Dear Marie Curie,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n                html: \"Dear Marie Curie,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n                },\n        },\n        Email {\n            sender: \"albert@einstein.com\",\n            receiver: \"alexandre@grothendieck.com\",\n            message: Message {\n                subject: \"Issue No. 1\",\n                text: \"Dear Alexandre Grothendieck,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n                html: \"Dear Alexandre Grothendieck,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n            },\n        },\n        Email {\n            sender: \"albert@einstein.com\",\n            receiver: \"emmy@noether.com\",\n            message: Message {\n                subject: \"Issue No. 1\",\n                text: \"Dear Emmy Noether,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n                html: \"Dear Emmy Noether,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n            },\n        },\n        Email {\n            sender: \"albert@einstein.com\",\n            receiver: \"elie@cartan.com\",\n            message: Message {\n                subject: \"Issue No. 1\",\n                text: \"Dear Elie Cartan,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n                html: \"Dear Elie Cartan,\n                    Welcome to my newsletter. We are doing hard sciences here.\n                    Sincerely, Albert Einstein\",\n            },\n        },\n    ],\n}\n\u003e Should an email be sent to 4 recipients? Yes (y) or no (n)\n\u003e\n```\n\nConfirm `y` if you are ready to go.\n\n## How to connect\n\n### How to connect to SMTP server\n\nTo connect to a SMTP server, define environment variables `SMTP_SERVER`, `SMTP_USERNAME`, and `SMTP_PASSWORD`. For example, using AWS SES:\n\n``` bash\nSMTP_SERVER=email-smtp.eu-west-1.amazonaws.com\nSMTP_USERNAME=...\nSMTP_PASSWORD=...\n```\n\nSource your environment `.env` in your current shell:\n\n``` bash\nset -a \u0026\u0026 source .env \u0026\u0026 set +a\n```\n\n### How to connect to email provider API\n\nInstead of using SMTP, you can send emails via the API of a specific email provider as well.\n\nUsing AWS SES, define the following environment variables:\n\n``` bash\nAWS_ACCESS_KEY_ID=...\nAWS_SECRET_ACCESS_KEY=...\nAWS_REGION=eu-west-1\n```\n\nwhere `AWS_REGION` depends on the specified region for your AWS SES account.\n\nSource your environment again:\n\n``` bash\nset -a \u0026\u0026 source .env \u0026\u0026 set +a\n```\n\nSend an email using `--connection`:\n\n``` rust\npigeon send \\\n    sender@your-domain.com \\\n    receiver@gmail.com \\\n    --connection aws \\\n    --message-file \"message.yaml\"\n```\n\n### How to connect to postgres database\n\nFor postgres, the database url is constructed as follows: `postgresql://db_user:db_password@db_host:db_port/db_name`.\n\nTherefore, set the following environment variables in your environment `.env`:\n\n- `DB_HOST`\n- `DB_PORT`\n- `DB_USER`\n- `DB_PASSWORD`\n- `DB_NAME`\n\nSource your environment again:\n\n``` bash\nset -a \u0026\u0026 source .env \u0026\u0026 set +a\n```\n\n_CAUTION:_ Connecting via TLS is not supported yet. Forward a local port through a SSH tunnel instead, e.g.:\n\n``` bash\npigeon query \"select email from user where newsletter_confirmed = true\" --display --ssh-tunnel 5437\n```\n\nIn addition to the environment variables above, `SERVER_USER` and `SERVER_HOST` have to be set for the SSH connection (`ssh user@host`).\n\n## Integrations\n\n### Email protocols\n\n- MIME\n- SMTP\n\n### Third-party APIs\n\n- AWS SES\n\n### Data sources\n\n- PostgreSQL\n- CSV\n\n## Comparison with Mailchimp, Sendgrid, and ConvertKit\n\nThese numbers may be outdated. Do your own research.\n\nThe following table compares the price per month for email provider and emails per month.\n\n\u0026nbsp; | 5,000 | 10,000 | 100,000\n--- | --- | --- | ---\n**Pigeon+**[**AWS**](https://aws.amazon.com/ses/pricing/) | $4.50 | $5 | $14\n[**Mailchimp Marketing**](https://mailchimp.com/pricing/marketing/) | $9.99 | $20.99 | $78.99\n[**Mailchimp Transactional**](https://mailchimp.com/pricing/transactional-email/) | - | - | $80\n[**Sendgrid Marketing**](https://sendgrid.com/pricing/) | $15 | $15 | $120\n[**Sendgrid API**](https://sendgrid.com/pricing/) | $14.95 | $14.95 | $29.95\n[**ConvertKit**](https://convertkit.com/pricing) | $66 | $100 | $516\n\nThe following table shows the daily limit for sent emails per provider.\n\nprovider | daily limit\n--------- | ---------\nPigeon+AWS | 50,000\nMailchimp | equals monthly limit\nSendgrid | equals monthly limit\n\n## Testing\n\nSome integration tests require a locally running postgres database:\n\n1. Specify the following environment variables:\n    - `DB_HOST`\n    - `DB_PORT`\n    - `DB_USER`\n    - `DB_PASSWORD`\n    - `DB_NAME`\n2. Set up a temporary postgres db: `docker-compose run --rm --service-ports postgres`\n\n``` bash\n# Run unit tests and integration tests\ncargo test\n\n# Run unit tests\ncargo test --lib\n\n# Run integration tests\ncargo test --test '*'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquambene%2Fpigeon-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquambene%2Fpigeon-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquambene%2Fpigeon-rs/lists"}