Python/Rust/Go/JavascriptでGmailを自動送信

エラー通知またお知らせを送信するために、メール自動送信機能を持つアプリが多いです。Python/Rust/Go/JavascriptでGmailを自動的に送信する機能を開発する手順をメモします。

メールを送信うする際、通信規定のSMTP(Simple Mail Transfer Protocol)があります。SMTPで使用されるデフォルトポートは587番ポートです。本記事でSMTPを設定する場合、587番ポートを使います。

![Screen Shot 2023-05-15 at 22.20.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2921051/40997442-51e1-eb5e-b0f0-62ac6b026ba1.png)



# Gmailアプリパスワードを設定

最初に、[Google アカウント](https://myaccount.google.com)を開いて、２段階認証をオンにします。次に、２段階認証でアプリパスワードを設定します。アプリパスワードは登録のパスワードではないです。メールを自動送信する際、アプリパスワードを使います。


![Screen Shot 2023-05-15 at 21.45.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2921051/18170385-c25a-ef7c-13dd-b8e3e1eda80b.png)

![Screen Shot 2023-05-15 at 21.45.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2921051/cfcff05c-e2fd-34dc-2694-a4aca5037f95.png)

アプリパスワードを設定した後、.envに送信メール、受信メールとアプリパスワードを保存します。

```.env
SENDER=sender@gmail.com
PASSWORD=apppassword
RECEIVER=receiver@gmail.com
```

# Javascriptで作成 
ここでnodemailerライブラリを使用します。非同期処理async...awaitを使います。

smtpを設定する際、nodemailer.createTransport()を使ってSMTPトランスポートを作ります。Gmailを送信しますので、host="smtp.gmail.com"とhost=587にします。




```js
// Reference: https://nodemailer.com/about/
"use strict";
const nodemailer = require("nodemailer");

//環境変数を取得
const dotenv = require('dotenv')
dotenv.config()

let mail_content = "Hello,This is a simple mail. There is only text, no attachments are there. The mail is sent using Js NodeEmail.Thank You!!!"

// async..await is not allowed in global scope, must use a wrapper
async function send_email(content) {

  // create reusable transporter object using the default SMTP transport
  let transporter = nodemailer.createTransport({
    host: "smtp.gmail.com",
    port: 587,
    secure: false, // true for 465, false for other ports
    auth: {
      user: process.env.SENDER, // user
      pass: process.env.PASSWORD, //  password
    },
  });

  // send mail with defined transport object
  let info = await transporter.sendMail({
    from: process.env.SENDER, // sender address
    to: process.env.RECEIVER, // list of receivers
    subject: "Test", // Subject line
    text: content, // plain text body
  });

}

send_email(mail_content).catch(console.error);

```

# Pythonで作成

Pythonにsmtplib（SMTPのライブラリ）が提供されています。メールをMIME形式に返還しなければなりません。

メッセージをMIME形式に変換する際、MIMEMultipart()でMIMEオブジェクトを作ります。　

SMTPサーバを設定する際、smtplib.SMTPを使います。


```python
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import os
from decouple import config

# Reference: https://www.tutorialspoint.com/send-mail-from-your-gmail-account-using-python

mail_content = '''
Hello,
This is a simple mail. There is only text, no attachments are there. The mail is sent using Python SMTP library.
Thank You!!!
'''

#The mail addresses and password
sender_addrs = config('SENDER')
sender_pass = config("PASSWORD")
receiver_addrs = config('RECEIVER')

#Setup the MIME
def set_message(sender_addrs, sender_pass, receiver_addrs, mail_content):
    message = MIMEMultipart()
    message['From'] = sender_addrs
    message['To'] = receiver_addrs
    message['Subject'] = 'A test mail sent by Python. It has an attachment.'   #The subject line

    #The body and the attachments for the mail
    message.attach(MIMEText(mail_content, 'plain'))

    return message

#Create SMTP session for sending the mail
def send_email_smtp(sender_addrs, receiver_addrs, message):
    session = smtplib.SMTP('smtp.gmail.com', 587) #use gmail with port
    session.starttls() #enable security
    session.login(sender_addrs, sender_pass) #login with mail_id and password
    text = message.as_string()
    session.sendmail(sender_addrs, receiver_addrs, text)
    session.quit()
    print('Mail Sent')

if __name__=="__main__":
    # MIME形式に変換
    message = set_message(sender_addrs, sender_pass, receiver_addrs, mail_content)
    # メール送信
    send_email_smtp(sender_addrs, receiver_addrs, message)

```

# Goで作成 

Golangに"net/smtp"を使う方法と"gomail"を使う方法があります。　

"net/smtp"を使う際、smtp.PlainAuthでAuthオブジェクトを作ります。次にsmtp.SendMailメソッドでメッセージを送信します。　　

"gomail"を使う際、gomail.NewMessageでメッセージオブジェクトを使います。メール送信メール、受信メールとコンテンツをメッセージオブジェクトで設定します。SMTPサーバを設定する際、gomail.NewDialerを使います。


```go 
// Reference: https://www.loginradius.com/blog/engineering/sending-emails-with-golang/
package main

import (
  "fmt"
  "net/smtp"
  "os"
  "github.com/joho/godotenv"
  "log"
  "crypto/tls"
  gomail "gopkg.in/mail.v2"
)

func main() {
	godotenv_err := godotenv.Load()
	if godotenv_err != nil {
		log.Fatal("Error loading .env file")
	}
	
	// Sender data.
	sender := os.Getenv("SENDER")
	password := os.Getenv("PASSWORD")

	// Receiver email address.
	receiver := []string{
		os.Getenv("RECEIVER"),
	}

	// Message.
	message := []byte("Hello,This is a simple mail. There is only text, no attachments are there. The mail is sent using Golang. Thank You!!!")
	// send_email_smtp(sender, password, receiver, message)

	send_email_gomail(sender, password, receiver, message)
	
}

func send_email_smtp(sender string, password string, receiver []string, message []byte) {
	// smtp server configuration.
	smtpHost := "smtp.gmail.com"
	smtpPort := "587"

	// Authentication.
	auth := smtp.PlainAuth("", sender, password, smtpHost)
		
	// Sending email.
	err := smtp.SendMail(smtpHost+":"+smtpPort, auth, sender, receiver, message)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Email Sent Successfully!")
}

func send_email_gomail(sender string, password string, receiver []string, message []byte) {
  mail := gomail.NewMessage()

  // Set E-Mail sender
  mail.SetHeader("From", sender)

  // Set E-Mail receivers
  mail.SetHeader("To", receiver[0])

  // Set E-Mail subject
  mail.SetHeader("Subject", "Gomail test subject")

  // Set E-Mail body. You can set plain text or html with text/html
  mail.SetBody("text/plain", string(message[:]))

  // Settings for SMTP server
  server := gomail.NewDialer("smtp.gmail.com", 587, sender, password)

  // This is only needed when SSL/TLS certificate is not valid on server.
  // In production this should be set to false.
  server.TLSConfig = &tls.Config{InsecureSkipVerify: true}

  // Now send E-Mail
  if err := server.DialAndSend(mail); err != nil {
    fmt.Println(err)
    panic(err)
  }
}
```


# Rustで作成　

mail_sendとmail_builderライブラリを使います。Javascriptと同じで、非同期処理を使います。　

まず、mail_builder::MessageBuilderでメッセージオブジェクトを作ります。このオブジェクトは送信メール、受信メールとメールコンテンツの情報を持ちます。　

mail_send::SmtpClientBuilderを使い、SMTPクライアントを作ります。次に、パスワードを使ってcredentialsを設定します。最後にメールを送信します。



```rust
use dotenv;
use std::env;
use mail_builder::MessageBuilder;
use mail_send::SmtpClientBuilder;

#[tokio::main]
async fn main() {

    dotenv::dotenv().ok();
    let sender = match env::var("SENDER"){
        Ok(val) => val,
        Err(e) => panic!("could not find {}: {}", "SENDER", e),
    } ;

    let password = match env::var("PASSWORD"){
        Ok(val) => val,
        Err(e) => panic!("could not find {}: {}", "PASSWORD", e),
    };
    
    let receiver = match env::var("RECEIVER") {
        Ok(val) => val,
        Err(e) => panic!("could not find {}: {}", "RECEIVER", e),
    };
    

    let content = "Hello,This is a simple mail. There is only text, no attachments are there. The mail is sent using Rust. Thank You!!!"; 
    send_email_smtp(&sender, &password, &receiver, content).await;
    
}


async fn send_email_smtp(sender:&str, password:&str, receiver:&str, content:&str) {
    // Build a simple multipart message
    let message = MessageBuilder::new()
    .from(("", sender))
    .to(vec![
        ("", receiver),
    ])
    .subject("Test!")
    .text_body(content);

    // Connect to the SMTP submissions port, upgrade to TLS and
    // authenticate using the provided credentials.
    SmtpClientBuilder::new("smtp.gmail.com", 587)
    .implicit_tls(false)
    .credentials((sender, password))
    .connect()
    .await
    .unwrap()
    .send(message)
    .await
    .unwrap();
}

```

# 参考 

[1] https://nodemailer.com/about/
[2] https://www.tutorialspoint.com/send-mail-from-your-gmail-account-using-python
[3] https://www.loginradius.com/blog/engineering/sending-emails-with-golang/
[4] https://docs.rs/mail-send/latest/mail_send/