diff --git a/Readme.md b/Readme.md index 4ff4837..24e7066 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,8 @@ [![Rust Build](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/rust.yml/badge.svg)](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/rust.yml) +[![Test](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/test.yml/badge.svg)](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/actions/workflows/test.yml) + + [![GitHub version](https://img.shields.io/github/v/tag/MinaMatta98/Bulk-Email-Cli-CLient?label=Version)](https://github.com/MinaMatta98/Bulk-Email-Cli-CLient/releases) @@ -15,26 +18,23 @@ An asynchronous [RUST](https://www.rust-lang.org/) based CLI bulk emailing client, built on top of [lettre-rs](https://github.com/lettre/lettre). -### Features ---- +## Features |Feature|Description| |-------|-----------| +|Template Variable Substitution| Refer to the [Template Variable Substitution](#template-variable-substitution) section for modifying email-templates| |Single Email Support| Refer to [Send a Single Email](#send-a-single-email)| |Bulk Email Support| Support for asynchronous unordered sending of bulk emails detailed within a csv file as described within [Send Bulk Emails](#send-bulk-emails) | |Attachment Support| Support for Inline/Standard Attachments | |SMTP Transport| This tool uses the SMTP protocol to send emails over the network | |TLS Encryption| This tool defaults to TLS encryption over network| -
-


-### Instructions: ---- +## Instructions: -#### Installation: +### Installation: Installation of this tool will require [RUST installation version](https://www.rust-lang.org/tools/install) of 1.70 or newer. To install this package via cargo, run the following command from a shell of choice: @@ -42,13 +42,10 @@ To install this package via cargo, run the following command from a shell of cho ```bash cargo install --locked --git https://github.com/MinaMatta98/Bulk-Email-Cli-CLient.git ``` - -
-


-#### Setting Environmental Variables +### Setting Environmental Variables --- The following environmental variables must be set: @@ -58,17 +55,15 @@ The following environmental variables must be set: |SENDER_PASSWORD|Corrosponding password for the email account sending Email(s).Turn on Less-Secure-Apps to send emails: https://myaccount.google.com | |SMTP_RELAY|The SMTP relay corrosponding to the SMTP Gateway of Choice. For example, Gmail uses smtp.gmail.com and AOL uses smtp.aol.com | -
-


-#### Send a Single Email +### Send a Single Email --- Refer to [Environmental Variables](#environmental-variables) for setting up environmental variables. -##### Scenario 1: +#### Scenario 1 Send an email to `minamatta98@gmail.com` with the template saved within `./templates/email.html` relative to the current working directory (check with ```bash pwd```). ```bash @@ -77,22 +72,21 @@ email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Tes
-##### Scenario 2: +#### Scenario 2 Send an email to `minamatta98@gmail.com` with the template saved within `./templates/email.html` relative to the current working directory (check with ```bash pwd```). This is to also send a pdf attachment within the example directory relative to the root directory of this repository. ```bash -email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" --attachment-1-path "./example/rust.pdf" +email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" attachments --attachment-1-path "./example/rust.pdf" ```
-##### Scenario 3: +#### Scenario 3 Send an email to `minamatta98@gmail.com` with the template saved within `./templates/email.html` relative to the current working directory (check with ```bash pwd```). -This is to also send an inline email attachment located within `./example/send_mail_7590.png` corrosponding to an inline content id of 2335. +This is to also send an inline email attachment located within `./example/send_mail_7590.png` corresponding to an inline content id of 2335. ```bash -email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" --attachment-1-path "./example/send_mail_7590.png" --attachment-1-inline-content-id 2335 -``` +email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/email.html" attachments --attachment-1-path "./example/send-mail-7590.svg" --attachment-1-inline-content-id 2335```
@@ -107,13 +101,14 @@ email-sender single-email --to-addr "minamatta98@gmail.com" --subject "Email Tes

-#### Send Bulk Emails +### Send Bulk Emails --- All three scenarios are listed within their respective order within the following table. -This will corrospond to the following csv file: +This will correspond to the following csv file: -_Note that unfilled fields must still be comma seperated_ +> _Note that unfilled fields must still be comma separated._ +> _Also note that attachment and substitution headers can be omitted where not needed_ ```csv DeliveryAddress,Subject,RelativeEmailTemplatePath,Attachment1Path,Attachment1InlineContentId @@ -128,3 +123,47 @@ To send bulk emails from a csv file saved within `./example/example.csv`, run th email-sender bulk-email --csv-file "./example/example.csv" ``` +
+
+ +### Template Variable Substitution +--- +The following variables can be used within an email-template and encoded as `{{field name}}` to be replaced: + +| Field Name | Description |Csv Header | +|----------------------|------------------------------------------------------------------------------------------------------| ----------| +| `name` | Replace instance of `{{name}}` within email-template to `--name` value |Name| +| `email` | Replace instance of `{{email}}` within email-template to `--email` value |Email| +| `date` | Replace instance of `{{date}}` within email-template to `--date` value |Date| +| `time` | Replace instance of `{{time}}` within email-template to `--time` value |Time| +| `order_number` | Replace instance of `{{order_number}}` within email-template to `--order-number` value |OrderNumber| +| `product_information`| Replace instance of `{{product_information}}` within email-template to `--product-information` value| ProductInformation | +| `location` | Replace instance of `{{location}}` within email-template to `--location` value |Location| +| `username` | Replace instance of `{{user_name}}` within email-template to `--user-name` value |Username| +| `confirmation_link` | Replace instance of `{{confirmation_link}}` within email-template to `--confirmation-link` value |ConfirmationLink| +| `password_reset_link`| Replace instance of `{{password_reset_link}}` within email-template to `--password-reset-link` value |PasswordResetLink| +| `discount_code` | Replace instance of `{{discount_code}}` within email-template to `--discount-code` value |DiscountCode| +| `temp_code` | Replace instance of `{{temp_code}}` within email-template to `--temp-code` value |TempCode| +| `course_details` | Replace instance of `{{course_details}}` within email-template to `--course-details` value |CourseDetails| + +
+ +#### Single Email Substitution Example +In this example, instances of `{{name}}` within the email-template will be replaced with Mina and instances of ``{{temp_code}}`` will be replaced with 123456 + +```bash +cargo run --release -- single-email --to-addr "minamatta98@gmail.com" --subject "Email Testing CLI" --email-template "./templates/test.html" attachments --attachment-1-path "./example/send-mail-7590.svg" --attachment-1-inline-content-id 2335 token --name "Mina" --temp-code 123456 +``` + +
+ +#### Bulk Email Substitution Example +The following example accomplishes the same function as the [single email substitution example](#single-email-substitution-example). + +> _Note that variables are evaluated separately for each row entry and can be left blank if not needed._ +> _Also note that field names for substitutions can be left empty if not needed. For example, the Name and TempCode header are not required if values are not passed._ + +```csv +DeliveryAddress,Subject,RelativeEmailTemplatePath,Attachment1Path,Attachment1InlineContentId,Name,TempCode +minamatta98@gmail.com,Email Template Tests,./templates/replacement_test.html,./example/send_mail_7590.png,2335,Mina,123456 +``` diff --git a/src/cli.rs b/src/cli.rs index 0116e93..6763564 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -82,7 +82,7 @@ macro_rules! commands_struct { #[serde(flatten)] #[command(subcommand)] - replacement_tokens: ReplacementTokens, + replacement_tokens: Option, }, } } @@ -125,7 +125,7 @@ pub struct EmailInfo { /// Optional Inline File Attachment #[serde(flatten)] #[command(subcommand)] - pub attachment: AttachmentCommand, + pub attachment: Option, } commands_struct!(10); @@ -148,7 +148,7 @@ mod test { attachment_1_path, attachment_1_inline_content_id, .. - } = record.attachment; + } = record.attachment.unwrap(); assert!(attachment_1_path.is_some()); assert!(attachment_1_inline_content_id.is_some()); diff --git a/src/email.rs b/src/email.rs index 1253e35..388c943 100644 --- a/src/email.rs +++ b/src/email.rs @@ -54,7 +54,7 @@ macro_rules! email_struct { subject: String, email_body_location: PathBuf, #[serde(flatten)] - attachment: AttachmentCommand, + attachment: Option, } impl Email { @@ -63,7 +63,7 @@ macro_rules! email_struct { delivery_address: String, subject: String, email_body_location: PathBuf, - attachment: AttachmentCommand, + attachment: Option, ) -> Self { Email { delivery_address, @@ -129,46 +129,60 @@ macro_rules! email_struct { let mut email = std::fs::read_to_string(email_body_location)?; + let mut multi_part: MultiPart; + #[allow(clippy::collapsible_match)] - let AttachmentCommand::Attachments { + if let Some(AttachmentCommand::Attachments { $( [], [], )* replacement_tokens + }) + = attachment { + + if let Some(replacement_tokens) = replacement_tokens { + replacement_tokens.replace_all_substrings(&mut email); + } + + multi_part = MultiPart::related().singlepart( + SinglePart::builder() + .header(header::ContentType::TEXT_HTML) + .body(email), + ); + + + + $( + if let Some([]) = [] && &[].display().to_string() != "" { + let attachment = if let Some([]) = [] && [] != String::new() { + Attachment::new_inline( + [].to_string().replace('"', "") + ) + .body( + std::fs::read(&[< attachment_ $no _path>])?, + ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?, + ) + + } else { + Attachment::new([< attachment_ $no _path>].file_name().unwrap().to_str().unwrap().to_string()) + .body( + std::fs::read(&[< attachment_ $no _path>])?, + ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?, + ) + + }; + multi_part = multi_part.singlepart(attachment); + }; + )* + + } else { + multi_part = MultiPart::related().singlepart( + SinglePart::builder() + .header(header::ContentType::TEXT_HTML) + .body(email), + ); } - = attachment; - - replacement_tokens.replace_all_substrings(&mut email); - - let mut multi_part = MultiPart::related().singlepart( - SinglePart::builder() - .header(header::ContentType::TEXT_HTML) - .body(email), - ); - - $( - if let Some([]) = [] && &[].display().to_string() != "" { - let attachment = if let Some([]) = [] && [] != String::new() { - Attachment::new_inline( - [].as_number().unwrap().to_string() - ) - .body( - std::fs::read(&[< attachment_ $no _path>])?, - ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?, - ) - - } else { - Attachment::new([< attachment_ $no _path>].file_name().unwrap().to_str().unwrap().to_string()) - .body( - std::fs::read(&[< attachment_ $no _path>])?, - ContentType::parse(&mime_guess::from_path([< attachment_ $no _path>]).first().unwrap().to_string())?, - ) - - }; - multi_part = multi_part.singlepart(attachment); - }; - )* let email = Message::builder() .from(from_addr.parse()?) diff --git a/src/tokens.rs b/src/tokens.rs index 56f9ef2..07d9d4a 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -172,9 +172,9 @@ mod test { let AttachmentCommand::Attachments { replacement_tokens, .. - } = record.attachment; + } = record.attachment.unwrap(); - let ReplacementTokens::Token { replacement_tokens } = replacement_tokens; + let ReplacementTokens::Token { replacement_tokens } = replacement_tokens.unwrap(); replace_html_file_substrings(replacement_tokens)?; }