Command line tool for cheap and efficient email automation written in Rust

Overview

Pigeon

Pigeon 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.

For example, you can define a bash alias with your individual pigeon command to send your weekly newsletter to your audience. You might want to automatize the send schedule by defining a systemd .service.

You might also draft a static html with your favorite web development framework, and use pigeon to send this html template.

Requirements

You need to have Rust installed on your system and nightly toolchain activated.

Install Pigeon

Install Pigeon from crates.io

# Install nightly toolchain
rustup toolchain install nightly

# Switch to nightly toolchain
rustup override set nightly

# Build and install pigeon binary to ~/.cargo/bin
cargo install pigeon-rs

Install Pigeon from github.com

rust-toolchain # Build and install pigeon binary to ~/.cargo/bin cargo install --path . ">
# Clone repository
git clone [email protected]:quambene/pigeon-rs.git
cd pigeon-rs

# Activate rust nightly toolchain for current directory
echo "nightly" > rust-toolchain

# Build and install pigeon binary to ~/.cargo/bin
cargo install --path .

Note: Add $HOME/.cargo/bin to your PATH if it is missing:

export PATH="$HOME/.cargo/bin:$PATH"

Usage

Check connection to your email provider with pigeon connect. For example, using AWS Simple Email Service (SES):

pigeon connect aws

Connected to aws client: ok

See currently supported integrations and how to connect below.

Send email to a single receiver

Send a single email with subject and content:

pigeon send [email protected] [email protected] --subject "Test subject" --content "This is a test email."

Send a single email with message defined in separate template file:

pigeon send [email protected] [email protected] --message-file "message.yaml"

The message template message.yaml is created with subcommand init:

pigeon init

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 --object the same format will be sent as plaintext and html email.

Send bulk email to multiple receivers

For example, query relevant users which confirmed to receive your newsletter, and send an email to all of them.

Let's check the query first via pigeon query:

pigeon query --display "select email from user where newsletter_confirmed = true"
> Display query result: shape: (4, 1)
+------------------------------+
| email                        |
| ---                          |
| str                          |
+==============================+
| "[email protected]"            |
+------------------------------+
| "[email protected]" |
+------------------------------+
| "[email protected]"           |
+------------------------------+
| "[email protected]"            |
+------------------------------+

See how to connect below to connect your database.

Note: You can also --save your query as a csv file: pigeon query --save .

Now send your newsletter to the queried receivers. Let's try a --dry-run without confirmation --assume-yes first:

pigeon send-bulk [email protected] --receiver-query "select email from user where newsletter_confirmed = true" --message-file "message.yaml" --assume-yes --dry-run
> Sending email to 4 receivers ...
[email protected] ... dry run
[email protected] ... dry run
[email protected] ... dry run
[email protected] ... dry run

After double checking, you can submit the same command without --dry-run. Remove --assume-yes as well for explicit confirmation.

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.

Personalize your emails

If you need more individual emails, you can personalize your emails with option --personalize. Again, let's start by checking the relevant query:

pigeon query --display "select first_name, last_name, email from user where newsletter_confirmed = true"
> Display query result: shape: (4, 3)
+-------------+----------------+------------------------------+
| first_name  | last_name      | email                        |
| ---         | ---            | ---                          |
| str         | str            | str                          |
+=============+================+==============================+
| "Marie"     | "Curie"        | "[email protected]"            |
+-------------+----------------+------------------------------+
| "Alexandre" | "Grothendieck" | "[email protected]" |
+-------------+----------------+------------------------------+
| "Emmy"      | "Noether"      | "[email protected]"           |
+-------------+----------------+------------------------------+
| "Elie"      | "Cartan"       | "[email protected]"            |
+-------------+----------------+------------------------------+

In 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:

pigeon send-bulk [email protected] --receiver-query "select first_name, last_name, email from user where newsletter_confirmed = true" --message-file "message.yaml" --personalize "first_name" "last_name" --display
Display emails: BulkEmail { emails: [ Email { sender: "[email protected]", receiver: "[email protected]", message: Message { subject: "Issue No. 1", text: "Dear Marie Curie, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", html: "Dear Marie Curie, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", }, }, Email { sender: "[email protected]", receiver: "[email protected]", message: Message { subject: "Issue No. 1", text: "Dear Alexandre Grothendieck, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", html: "Dear Alexandre Grothendieck, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", }, }, Email { sender: "[email protected]", receiver: "[email protected]", message: Message { subject: "Issue No. 1", text: "Dear Emmy Noether, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", html: "Dear Emmy Noether, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", }, }, Email { sender: "[email protected]", receiver: "[email protected]", message: Message { subject: "Issue No. 1", text: "Dear Elie Cartan, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", html: "Dear Elie Cartan, Welcome to my newsletter. We are doing hard sciences here. Sincerely, Albert Einstein", }, }, ], } > Should an email be sent to 4 recipients? Yes (y) or no (n) > ">
> Display message file: MessageTemplate {
    message: Message {
        subject: "Issue No. 1",
        text: "Dear {first_name} {last_name},
            Welcome to my newsletter. We are doing hard sciences here.
            Sincerely, Albert Einstein",
        html: "Dear {first_name} {last_name},
            Welcome to my newsletter. We are doing hard sciences here.
            Sincerely, Albert Einstein",
    },
}
> Display emails: BulkEmail {
    emails: [
        Email {
            sender: "[email protected]",
            receiver: "[email protected]",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Marie Curie,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Marie Curie,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                },
        },
        Email {
            sender: "[email protected]",
            receiver: "[email protected]",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Alexandre Grothendieck,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Alexandre Grothendieck,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
            },
        },
        Email {
            sender: "[email protected]",
            receiver: "[email protected]",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Emmy Noether,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Emmy Noether,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
            },
        },
        Email {
            sender: "[email protected]",
            receiver: "[email protected]",
            message: Message {
                subject: "Issue No. 1",
                text: "Dear Elie Cartan,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
                html: "Dear Elie Cartan,
                    Welcome to my newsletter. We are doing hard sciences here.
                    Sincerely, Albert Einstein",
            },
        },
    ],
}
> Should an email be sent to 4 recipients? Yes (y) or no (n)
>

Confirm y if you are ready to go.

How to connect

How to connect to email provider

For AWS SES, define environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Source your environment .env in your current shell:

set -a && source .env && set +a

How to connect to postgres database

For postgres, the database url is constructed as follows: postgresql://db_user:db_password@db_host:db_port/db_name.

Therefore, set the following environment variables in your environment .env:

  • DB_HOST
  • DB_PORT
  • DB_USER
  • DB_PASSWORD
  • DB_NAME

Source your environment again:

set -a && source .env && set +a

CAUTION: Connecting via TLS is not supported yet. Forward a local port through a SSH tunnel instead, e.g.:

pigeon query "select email from user where newsletter_confirmed = true" --display --ssh-tunnel 5437

In addition to the environment variables above, SERVER_USER and SERVER_HOST have to be set for the SSH connection (ssh user@host).

Integrations

Email provider

  • AWS SES

Data sources

  • PostgreSQL
  • CSV

Comparison with Mailchimp and Sendgrid

These numbers may be outdated. Do your own research.

emails/month Pigeon+AWS* Mailchimp Marketing* Sendgrid API* Sendgrid Marketing*
5,000 $4 $9.99 $14.95 $15
10,000 $4 $20.99 $14.95 $15
100,000 $4 $78.99 $29.95 $120

*Price per month

provider daily limit
Pigeon+AWS 50,000
Mailchimp equals monthly limit
Sendgrid equals monthly limit
You might also like...
A Python package written in Rust for email verification without sending any emails.

PyRustify PyRustify is a Python package written in Rust that verifies the email addresses. Features Feature Description Syntax validation Checks if th

Holo is a suite of routing protocols designed to support high-scale and automation-driven networks.

Holo is a suite of routing protocols designed to support high-scale and automation-driven networks. For a description of what a routing protocol is, p

Pink is a command-line tool inspired by the Unix man command.

Pink is a command-line tool inspired by the Unix man command. It displays custom-formatted text pages in the terminal using a subset of HTML-like tags.

REC2 (Rusty External Command and Control) is client and server tool allowing auditor to execute command from VirusTotal and Mastodon APIs written in Rust. 🦀
REC2 (Rusty External Command and Control) is client and server tool allowing auditor to execute command from VirusTotal and Mastodon APIs written in Rust. 🦀

Information: REC2 is an old personal project (early 2023) that I didn't continue development on. It's part of a list of projects that helped me to lea

A CI inspired approach for local job automation.

nauman A CI inspired approach for local job automation. Features • Installation • Usage • FAQ • Examples • Job Syntax About nauman is an easy-to-use j

A standalone Command Line Interface debugging tool for The Witcher 3 written in Rust
A standalone Command Line Interface debugging tool for The Witcher 3 written in Rust

A standalone Command Line Interface debugging tool for The Witcher 3 written in Rust. This tool is intended for Witcher 3 modders who make mainly scri

A command line tool, manage your hundreds of repository, written with Rust

A command line tool, manage your hundreds of repository, written with Rust

Command-line HTTP client for sending a POST request to specified URI on each stdin line.

line2httppost Simple tool to read lines from stdin and post each line as separate POST request to a specified URL (TCP connection is reused though). G

Efficient scan conversion of a line segment with clipping to a rectangular window.
Efficient scan conversion of a line segment with clipping to a rectangular window.

✂️ clipline 📏 clipline is a Rust crate for efficient scan conversion of a line segment with clipping to a rectangular window. It is an implementation

Comments
  • Invalid Base64 encoding for AWS

    Invalid Base64 encoding for AWS

    I'm testing pigeon with AWS and I keep receiving the following error:

      <Error>
        <Type>Sender</Type>
        <Code>MalformedInput</Code>
        <Message>Invalid Base64 encoding</Message>
      </Error>
    

    Here is the command that I am running (actual email addresses hidden for privacy):

    pigeon send --connection aws --verbose --display [email protected] [email protected] --message-file message.yaml
    

    And here is the debug output:

    Reading message file 'message.yaml' ...
    Display message file: MessageTemplate {
        subject: "Pigeon Test",
        text: Some(
            "Testing pigeon for sending email.",
        ),
        html: Some(
            "Testing pigeon for sending email.",
        ),
    }
    Display email: Email {
        sender: "[email protected]",
        receiver: "[email protected]",
        message: Message {
            subject: "Pigeon Test",
            text: Some(
                "Testing pigeon for sending email.",
            ),
            html: Some(
                "Testing pigeon for sending email.",
            ),
        },
        mime_format: From: [email protected]
        To: [email protected]
        Subject: Pigeon Test
        MIME-Version: 1.0
        Date: Mon, 04 Jul 2022 18:21:37 +0000
        Content-Type: multipart/alternative;
         boundary="QtdoKChvSXDqIPYoKulOOZ8UFmHlV30mqzhHqI1G"
    
        --QtdoKChvSXDqIPYoKulOOZ8UFmHlV30mqzhHqI1G
        Content-Type: text/plain; charset=utf-8
        Content-Transfer-Encoding: 7bit
    
        Testing pigeon for sending email.
        --QtdoKChvSXDqIPYoKulOOZ8UFmHlV30mqzhHqI1G
        Content-Type: text/html; charset=utf-8
        Content-Transfer-Encoding: 7bit
    
        Testing pigeon for sending email.
        --QtdoKChvSXDqIPYoKulOOZ8UFmHlV30mqzhHqI1G--
        ,
    }
    

    It seems that the message is not being encoded as Base64.

    I'll take another look at the source and see if I can further isolate or fix the error, time permitting.

    opened by egrieco 5
  • Missing conversion rule for bytea in postgres

    Missing conversion rule for bytea in postgres

    Error message:

    Can't run dispatcher: ConnectorX(NoConversionRule("ByteA(true)", "connectorx::destinations::arrow::typesystem::ArrowTypeSystem"))

    Linked to https://github.com/sfu-db/connector-x/issues/148 and https://github.com/pola-rs/polars/issues/1805.

    opened by quambene 1
  • Not working to set SMTP in fish shell

    Not working to set SMTP in fish shell

    Hi, Just wanted to try this out for some bulk emails and wanted to connect to gmail over smtp. When I use SMTP_SERVER=smtp.gmail.com I get this error: fish: Unsupported use of '='. In fish, please use 'set SMTP_SERVER smtp.gmail.com'. I then use that command, aka set SMTP_something something for server username and password but when I go to pigeon connect smtp I get this error

    Error: Missing environment variable 'SMTP_SERVER'
    
    Caused by:
        environment variable not found
    
    

    My guess is that it is caused by me using fish for some reason. And yes I did run set -a && source .env && set +a and yes the variables are set in the verbose output.

    opened by AlbinSjoegren 2
Owner
I use Rust btw
null
A simple to use and efficient Web Automation Tool.

teemo A simple to use and efficient Web Automation Tool. teemo allows you to do some web automation action(such as click and so on) and crawl some inf

null 3 Nov 22, 2023
Small command-line tool to switch monitor inputs from command line

swmon Small command-line tool to switch monitor inputs from command line Installation git clone https://github.com/cr1901/swmon cargo install --path .

William D. Jones 5 Aug 20, 2022
Tool written in Rust to enumerate the valid email addresses of an Azure/Office 365 Tenant

AzureEmailChecker Tool written in Rust to enumerate the valid email addresses of an Azure/Office 365 Tenant. It is multi threaded and makes no connect

Pierre 11 Feb 27, 2024
botwork is a single-binary, generic and open-source automation framework written in Rust for acceptance testing & RPA

botwork botwork is a single-binary, generic and open-source automation framework written in Rust for acceptance testing, acceptance test driven develo

Nitimis 8 Apr 17, 2023
A file management automation tool.

organize A file management automation tool. Current Status This is in really early development. Please come back later! Background The Python organize

null 4 Jun 6, 2023
A robust, customizable, blazingly-fast, efficient and easy-to-use command line application to uwu'ify your text!

uwuifyy A robust, customizable, blazingly-fast, efficient and easy-to-use command line application to uwu'ify your text! Logo Credits: Jade Nelson Tab

Hamothy 43 Dec 12, 2022
Open source email client written in Rust and Dioxus. Under 🏗️

Blazemail A full-featued, beautiful, mail client that doesn't suck. Works on mac, windows, linux, mobile, web, etc. Features, status Blazemail is curr

Jon Kelley 13 Dec 19, 2022
A command line tool written in Rust and designed to be a modern build tool + package manager for C/C++ projects.

CCake CCake is a command line tool written in Rust and designed to be a modern build tool + package manager for C/C++ projects. Goals To be easily und

Boston Vanseghi 4 Oct 24, 2022
Nodium is an easy-to-use data analysis and automation platform built using Rust, designed to be versatile and modular.

Nodium is an easy-to-use data analysis and automation platform built using Rust, designed to be versatile and modular. Nodium aims to provide a user-friendly visual node-based interface for various tasks.

roggen 19 May 2, 2023
Email test server for development, written in Rust

MailCrab Email test server for development, written in Rust. Inspired by MailHog and MailCatcher. MailCrab was created as an exercise in Rust, trying

Tweede golf 30 Feb 7, 2023