I am using https://github.com/google/google-api-php-client and I want to send a test email with a user's authorized gmail account.
This is what I have so far:
$msg = new Google_Service_Gmail_Message();
$msg->setRaw('gp1');
$service->users_messages->send('me', $msg);
This results in a bounce email because I have no clue how to set the raw message. I see the bounce in the inbox of my authenticated user. I want to learn how to set values for 'To', 'Cc', 'Bcc', 'Subject', and 'Body' of the email. I believe I will need to do a 64 encoding on that raw data as well. And I might want to use some html in the body of my email.
Please help to provide a working example of sending an email using the gmail-api and the google-api-php-client.
Here is the bounced email in the inbox:
Bounce -nobody#gmail.com- 12:58 PM (7 minutes ago)
to me
An error occurred. Your message was not sent.
‚ Date: Thu, 24 Jul 2014 10:58:30 -0700 Message-Id: CABbXiyXhRBzzuaY82i9iODEiwxEJWO1=jCcDM_TH-
I asked a more specific question which has led me to an answer. I am now using PHPMailer to build the message. I then extract the raw message from the PHPMailer object. Example:
require_once 'class.phpmailer.php';
$mail = new PHPMailer();
$mail->CharSet = "UTF-8";
$subject = "my subject";
$msg = "hey there!";
$from = "myemail#gmail.com";
$fname = "my name";
$mail->From = $from;
$mail->FromName = $fname;
$mail->AddAddress("tosomeone#somedomain.com");
$mail->AddReplyTo($from,$fname);
$mail->Subject = $subject;
$mail->Body = $msg;
$mail->preSend();
$mime = $mail->getSentMIMEMessage();
$m = new Google_Service_Gmail_Message();
$data = base64_encode($mime);
$data = str_replace(array('+','/','='),array('-','_',''),$data); // url safe
$m->setRaw($data);
$service->users_messages->send('me', $m);
I've used this solution as well, worked fine with a few tweaks:
When creating the PHPMailer object, the default encoding is set to '8bit'.
So I had to overrule that with:
$mail->Encoding = 'base64';
The other thing i had to do was tweaking the MIME a little to make it POST ready for the Google API, I've used the solution by ewein:
Invalid value for ByteString error when calling gmail send API with base64 encoded < or >
Anyway, this is how I've solved your problem:
//prepare the mail with PHPMailer
$mail = new PHPMailer();
$mail->CharSet = "UTF-8";
$mail->Encoding = "base64";
//supply with your header info, body etc...
$mail->Subject = "You've got mail!";
...
//create the MIME Message
$mail->preSend();
$mime = $mail->getSentMIMEMessage();
$mime = rtrim(strtr(base64_encode($mime), '+/', '-_'), '=');
//create the Gmail Message
$message = new Google_Service_Gmail_Message();
$message->setRaw($mime);
$message = $service->users_messages->send('me',$message);
Perhaps this is a bit beyond the original question, but at least in my case that "test email" progressed to regularly sending automated welcome emails "from" various accounts. Though I've found much that is helpful here, I've had to cobble things together from various sources.
In the hope of helping others navigate this process, here's a distilled version of what I came up with.
A few notes on the following code:
This assumes that you've jumped through the hoops to create and download the JSON for a Google Service Account (under credentials in the developer dashboard it's the bottom section.)
For clarity, I've grouped all the bits you should need to set in the "Replace this with your own data" section.
That me in the send() method is a key word that means something like "send this email with the current account."
// This block is only needed if you're working outside the global namespace
use \Google_Client as Google_Client;
use \Google_Service_Gmail as Google_Service_Gmail;
use \Google_Service_Gmail_Message as Google_Service_Gmail_Message;
// Prep things for PHPMailer
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
// Grab the needed files
require_once 'path/to/google-api/vendor/autoload.php';
require_once 'path/to/php-mailer/Exception.php';
require_once 'path/to/php-mailer/PHPMailer.php';
// Replace this with your own data
$pathToServiceAccountCredentialsJSON = "/path/to/service/account/credentials.json";
$emailUser = "sender#yourdomain.com"; // the user who is "sending" the email...
$emailUserName = "Sending User's Name"; // ... and their name
$emailSubjectLine = "My Email's Subject Line";
$recipientEmail = "recipient#somdomain.com";
$recipientName = "Recipient's Name";
$bodyHTML = "<p>Paragraph one.</p><p>Paragraph two!</p>";
$bodyText = "Paragraph one.\n\nParagraph two!";
// Set up credentials and client
putenv("GOOGLE_APPLICATION_CREDENTIALS={$pathToServiceAccountCredentialsJSON}");
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
// We're only sending, so this works fine
$client->addScope(Google_Service_Gmail::GMAIL_SEND);
// Set the user we're going to pretend to be (Subject is confusing here as an email also has a "Subject"
$client->setSubject($emailUser);
// Set up the service
$mailService = new Google_Service_Gmail($client);
// We'll use PHPMailer to build the raw email (since Google's API doesn't do that) so prep it
$mailBuilder = new PHPMailer();
$mailBuilder->CharSet = "UTF-8";
$mailBuilder->Encoding = "base64";
// Not set up the email you want to send
$mailBuilder->Subject = $emailSubjectLine;
$mailBuilder->From = $emailUser;
$mailBuilder->FromName = $emailUserName;
try {
$mailBuilder->addAddress($recipientEmail, $recipientName);
} catch (Exception $e) {
// Handle any problems adding the email address here
}
// Add additional recipients, CC, BCC, ReplyTo, if desired
// Then add the main body of the email...
$mailBuilder->isHTML(true);
$mailBuilder->Body = $bodyHTML;
$mailBuilder->AltBody = $bodyText;
// Prep things so we have a nice, raw message ready to send via Google's API
try {
$mailBuilder->preSend();
$rawMessage = base64_encode($mailBuilder->getSentMIMEMessage());
$rawMessage = str_replace(['+', '/', '='], ['-', '_', ''], $rawMessage); // url safe
$gMessage = new Google_Service_Gmail_Message();
$gMessage->setRaw($rawMessage);
// Send it!
$result = $mailService->users_messages->send('me', $gMessage);
} catch (Exception $e) {
// Handle any problems building or sending the email here
}
if ($result->labelIds[0] == "SENT") echo "Message sent!";
else echo "Something went wrong...";
Create the mail with phpmailer works fine for me in a local enviroment. On production I get this error:
Invalid value for ByteString
To solve this, remove the line:
$mail->Encoding = 'base64';
because the mail is two times encoded.
Also, on other questions/issues I found the next:
use
strtr(base64_encode($val), '+/=', '-_*')
instead of
strtr(base64_encode($val), '+/=', '-_,')
I Used https://developers.google.com/gmail/api/v1/reference/users/messages/send
and able to send email successfully.
Related
I want to send reply message from Gmail Api and it is going fine and the message is threaded or appended to the reciever mailbox (A & B user). But If I add new CC user (we name as C) then the new User should see threaded messages which was previously communicated between A & B.
Please help me out if anybody know the solution
<?php
$client = getClient();
$gmail = new Google_Service_Gmail($client);
$message = new Google_Service_Gmail_Message();
$optParam = array();
$referenceId = '';
$thread = $gmail->users_threads->get('someid#gmail.com', $threadId);
$optParam['threadId'] = '16c632fd24536690';
$threadMessages = $thread->getMessages($optParam);
$messageId = $threadMessages[0]->getId();
$subject = "Re: Thread mail test";
$mail = new PHPMailer();
$mail->CharSet = 'UTF-8';
$mail->From = $from_email;
$mail->FromName = $from_name;
$mail->addCustomHeader('In-Reply-To',
'<CAAdsdfsdf890sdjfklG4rJzoepBbWn3Crhq9sdfGq6kg#mail.gmail.com>');
$mail->addCustomHeader('References',
'<CAAdsdfsdf890sdjfklG4rJzoepBbWn3Crhq9sdfGq6kg#mail.gmail.com>');
$mail->addAddress($to);
$mail->addcc($cc);
$mail->Subject = $subject;
$mail->Body = $body;
$mail->preSend();
$mime = $mail->getSentMIMEMessage();
$raw = rtrim(strtr(base64_encode($mime), '+/', '-_'), '=');
$message->setRaw($raw);
$message->setThreadId($threadId);
$response = $gmail->users_messages->send($userId, $message);
?>
Using Google Apps Script instead of Gmail API
would allow you to use message.forward which would send all the messages of a thread. You could implement it by
List all thread messages with thread.getMessages()
Get the last thread message with threadMessages[threadMessages.length-1]
Forward the last thread message to desired recipients (A, B and C) with message.forward()
If you want to stick to Gmail API, a workaround I can think of would imply to
get all thread messages
Get their raw contents
Append the raw contents of each thread message to the body of the message to send
An example:
function myFunction() {
var myThread = GmailApp.getThreadById("PASTE HERE THE THREAD ID");
var threadMessages = MyThread.getMessages();
var lastMessage = threadMessages[threadMessages.length-1];
lastMessage.forward("emailA", "emailB", "emailC");
}
The Simple way to send the Message in thread is as
1. Message Body should be like this
2. Convert message to base64 encode.
3. Use https://developers.google.com/gmail/api/v1/reference/users/messages/send to send an email using thread-id.
4.enjoy coding.
I'm setting up an Azure-based site that requires an automated email response. As Azure does not natively allow this, I've tried using SendGrid. The code (based on their samples) works perfectly well on my own server (it returns the 202 status and the email is delivered), but not at all on Azure. Is there any known reason for this?
My code is as follows:
<?php
require ("sendgrid-php/sendgrid-php.php");
$from = new SendGrid\Email("DX PHP Test", "MY EMAIL ADDRESS");
$subject = "Sending with SendGrid is Fun";
$to = new SendGrid\Email("Dorian", "MY EMAIL ADDRESS");
$content = new SendGrid\Content("text/plain", "and easy to do anywhere, even with PHP");
$mail = new SendGrid\Mail($from, $subject, $to, $content);
$apiKey = "MY API KEY";
$sg = new \SendGrid($apiKey);
$response = $sg->client->mail()->send()->post($mail);
echo $response->statusCode();
?>
Obviously, I've included the proper API key and email addresses in my working version. The sendgrid-php folder has also been uploaded to the correct place and, as I said earlier, it works on my own server.
I've had a look around but can't find any responses that specifically answer this question. If this approach cannot work for some reason (and I'd like to know why!) can anyone suggest a different approach?
EDIT: The possible duplicate suggested doesn't work either! My SendGrid name and/or password were not recognised when I tried to use it. However, the instructions at this blog do actually work. This is what I did:
Set up a SendGrid account in Azure and note the username generated on the Configurations blade. The password is the same as the one you set the SendGrid account up with.
Download PHPMailer from Github and extract the PHPMailer-master folder to the same folder as the code you want to run.
Use the following code (edited slightly from the blog mentioned above) to send an email from Azure. Obviously, change the username and password to the ones from step 1 above. You'll also need to add a proper email address in the line that begins with '$mail->addAddress' to test that it works:
<?php
require 'PHPMailer-master/PHPMailerAutoload.php';
$mail = new PHPMailer;
//$mail->SMTPDebug = 3; // Enable verbose debug output
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.sendgrid.net'; // Specify main/backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'SENDGRID USERNAME'; // SMTP username
$mail->Password = 'SENDGRID PASSWORD'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable TLS/SSL encryption
$mail->Port = 587; // TCP port to connect to
$mail->From = 'email#contoso.com';
$mail->FromName = 'From SendGrid website';
$mail->addAddress('TEST EMAIL ADDRESS', 'A NAME'); // Add a recipient
$mail->WordWrap = 50; // Set word wrap to 50 characters
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = 'Here is the subject';
$mail->Body = 'This is the HTML message body <b>in bold!</b>';
if(!$mail->send()) {
echo 'Message could not be sent.';
echo 'Mailer Error: ' . $mail->ErrorInfo;
} else {
echo 'Message has been sent';
}
?>
In codeigniter, working with phpMailer, I am trying to use EasyPeasyICS to help build an email with an ics file and this is a portion of my code:
$invite = new EasyPeasyICS();
$invite->addEvent(strtotime($appointment_data['start_datetime']),strtotime($appointment_data['end_datetime']),
$service_data['name'],$message." ".$appointment_link,$appointment_link);
$mail = new PHPMailer();
$mail->From = $company_settings['company_email'];
$mail->FromName = $company_settings['company_name'];
$mail->AddAddress($receiver_address); // "Name" argument crushes the phpmailer class.
$mail->IsHTML(true);
$mail->CharSet = 'UTF-8';
$mail->Subject = $title;
$mail->Body = $email_html;
$mail->Ical = $invite->render(false);
if (!$mail->Send()) {
throw new Exception('Email could not been sent. Mailer Error (Line '
. __LINE__ . '): ' . $mail->ErrorInfo);
}
return TRUE;
}
So, what I get is my HTML email and no ICS file. If I modify $mail->Ical = $invite->render(true) then the ics file downloads to me and I see the ICS file wrapped around my HTML as it is supposed to be and it looks great. Why is it not sending out with the email?
Even with the latest version (5.2.22), you must set AltBody if you want to parse Ical. Because alternative (alt) message type is required to Ical to be loaded.
But if you specify Ical, your email WILL BE an event, and will be considered as it by email clients.
I have a PHP script and I want to send messages via the bcc attribute. But this PHP script sends the message but does not send as bcc. The bcc addresses appear in the email message.
// Construct the email
$recipient = 'me#mydomain.com';
$headers = array();
$headers['From'] = 'ME <me#mydomain.com>';
$headers['Subject'] = $subject;
$headers['Reply-To'] = 'no-reply#mydomain.com';
$headers['Bcc'] = 'abc#anotherdomain.com, xyz#thatdomain.com';
$headers['Return-Path'] = 'me#mydomain.com';
//$params['sendmail_args'] = '-fme#mydomain.com'; // this does not work
$body = 'message body';
// Define SMTP Parameters
$params = array();
$params['host'] = 'mail.mydomain.com';
$params['port'] = '25';
$params['auth'] = 'LOGIN';
$params['username'] = 'me#mydomain.com'; // this needs to be a legitimate mail account on the server and not an alias
$params['password'] = 'abcdef';
// Create the mail object using the Mail::factory method
include_once('Mail.php');
$mail_object =& Mail::factory('smtp', $params);
// Send the message
$mail_object->send($recipient, $headers, $body);
Here's something that I found: "If the Cc or Bcc lines appear in the message body, make sure you're separating header lines with a new line (\n) rather than a carriage return-new line (\r\n). That should come at the very end of the headers."
You might also want to try moving that header before or after others to see if it fixes things.
An even better solution would be to switch to phpMailer, which is supposedly a much, much better solution for sending mail through php.
Edit: I forgot I'd created the SendMail(); function myself, which is why the explanation doesn't mention at first what it does.
I'm having some trouble with PHPMailer (https://github.com/PHPMailer/PHPMailer) when attempting to send two emails, one directly after the other.
The script is almost completely 'out of the box', with only a few modifications such as a foreach loop to allow for multiple addresses, and everything still works perfectly.
However, if I attempt to call more than one instance of SendMail(); I get the error message:
Fatal error: Cannot override final method Exception::__clone() in .... online 0
Previously I was using the in-built mail(); function, which allowed me to use it as many times as I liked in quick succession , but it doesn't appear to be that simple with PHPmailer:
$to = me#me.com;
$to2 = me2#me2.com';
$headers = 'php headers etc';
$subject = 'generic subject';
$message = 'generic message';
mail($to, $subject, $message, $headers);
mail($to2, $subject, $message, $headers);
The above would result in two identical emails being sent to different people, however I can't easily replicate this functionality with PHPmailer.
Is there a way of stacking these requests so that I can send successive emails without it failing? Forcing the script to wait until the first email has been sent would also be acceptable, although not preferential.
As I mentioned I know it works when only one instance is called, but I don't seem to be able to re-use the function.
I haven't included the source code, although it is all available on the link provided above.
Thanks in advance
Edit as requested
// First Email
$to = array(
'test#test.com',
'test2#test.com',);
$subject = "Subject";
$message = $message_start.$message_ONE.$message_end;
sendMail();
// Second Email
$to = array(
'test#test.com',
'test2#test.com',);
$subject = "Subject";
$message = $message_start.$message_TWO.$message_end;
sendMail();
The above is how I want this to work, as it would work with mail();. The first email will work fine, the second will not.
SendMail() code
This is from the PHPmailer website, and is what is defined as SendMail();. The only difference from the example is the loop for AddAddress, and the inclusion of $to as a global variable.
$mail = new PHPMailer();
$mail->IsSMTP(); // set mailer to use SMTP
$mail->Host = "smtp1.example.com;smtp2.example.com"; // specify main and backup server
$mail->SMTPAuth = true; // turn on SMTP authentication
$mail->Username = "jswan"; // SMTP username
$mail->Password = "secret"; // SMTP password
$mail->From = "from#example.com";
$mail->FromName = "Mailer";
foreach($to as $to_add){
$mail->AddAddress($to_add); // name is optional
}
$mail->AddReplyTo("info#example.com", "Information");
$mail->WordWrap = 50; // set word wrap to 50 characters
$mail->AddAttachment("/var/tmp/file.tar.gz"); // add attachments
$mail->AddAttachment("/tmp/image.jpg", "new.jpg"); // optional name
$mail->IsHTML(true); // set email format to HTML
$mail->Subject = "Here is the subject";
$mail->Body = "This is the HTML message body <b>in bold!</b>";
$mail->AltBody = "This is the body in plain text for non-HTML mail clients";
if(!$mail->Send())
{
echo "Message could not be sent. <p>";
echo "Mailer Error: " . $mail->ErrorInfo;
exit;
}
echo "Message has been sent";
You haven't posted this code that lets me make this a complete conclusion, but from the Exception and the way you've defined an overriding class inside a function, you probably have class.phpmailer.php loading every time like this:
require('class.phpmailer.php');
or
include('class.phpmailer.php');
You should change that line to
require_once('class.phpmailer.php');
The reason you need to change it to require_once is so that PHP will not load the class file the second time when you try to create the new/second PHPMailer class. Otherwise, the line class PHPMailer throws the __clone() exception.
Added an example below:
<?php
/**
* This example shows how to send a message to a whole list of recipients efficiently.
*/
//Import the PHPMailer class into the global namespace
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
error_reporting(E_STRICT | E_ALL);
date_default_timezone_set('Etc/UTC');
require '../vendor/autoload.php';
//Passing `true` enables PHPMailer exceptions
$mail = new PHPMailer(true);
$body = file_get_contents('contents.html');
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->SMTPKeepAlive = true; // SMTP connection will not close after each email sent, reduces SMTP overhead
$mail->Port = 25;
$mail->Username = 'yourname#example.com';
$mail->Password = 'yourpassword';
$mail->setFrom('list#example.com', 'List manager');
$mail->addReplyTo('list#example.com', 'List manager');
$mail->Subject = 'PHPMailer Simple database mailing list test';
//Same body for all messages, so set this before the sending loop
//If you generate a different body for each recipient (e.g. you're using a templating system),
//set it inside the loop
$mail->msgHTML($body);
//msgHTML also sets AltBody, but if you want a custom one, set it afterwards
$mail->AltBody = 'To view the message, please use an HTML compatible email viewer!';
//Connect to the database and select the recipients from your mailing list that have not yet been sent to
//You'll need to alter this to match your database
$mysql = mysqli_connect('localhost', 'username', 'password');
mysqli_select_db($mysql, 'mydb');
$result = mysqli_query($mysql, 'SELECT full_name, email, photo FROM mailinglist WHERE sent = FALSE');
foreach ($result as $row) {
try {
$mail->addAddress($row['email'], $row['full_name']);
} catch (Exception $e) {
echo 'Invalid address skipped: ' . htmlspecialchars($row['email']) . '<br>';
continue;
}
if (!empty($row['photo'])) {
//Assumes the image data is stored in the DB
$mail->addStringAttachment($row['photo'], 'YourPhoto.jpg');
}
try {
$mail->send();
echo 'Message sent to :' . htmlspecialchars($row['full_name']) . ' (' . htmlspecialchars($row['email']) . ')<br>';
//Mark it as sent in the DB
mysqli_query(
$mysql,
"UPDATE mailinglist SET sent = TRUE WHERE email = '" .
mysqli_real_escape_string($mysql, $row['email']) . "'"
);
} catch (Exception $e) {
echo 'Mailer Error (' . htmlspecialchars($row['email']) . ') ' . $mail->ErrorInfo . '<br>';
//Reset the connection to abort sending this message
//The loop will continue trying to send to the rest of the list
$mail->getSMTPInstance()->reset();
}
//Clear all addresses and attachments for the next iteration
$mail->clearAddresses();
$mail->clearAttachments();
}
In addition to #Amr most excellent code.
In order to use this in a cron fasion, two adds are useful.
$mail-> SMTPDebug = true;
$mail-> Debugoutput = function( $str, $level ) {_log($str);};
The function _log is up to you. Writing to a file, to a database or wherever. I personally have reduced this to
$mail-> Debugoutput = function( $str, $level ) {if( $level===3 ) {_log( $str ); } };
to only write the more juicier messages
the solution is to reset recipients data like this:
$Mailer->clearAddresses()
use your own variable as an instance of PHPMailer (instead of $Mailer)
$Mailer->clearAddresses()
This is the solution to avoid multiple msj to be send to the same recipient.