MailChimp API 3.0 Batch update - Always pending, total_operations:0 - php

Trying to update a batch of emails. I think I've tried every way to do this, but my use of DrewM's MailChimp wrapper only returns the following $result content:
Array ( [id] => 1234abcd [status] => pending [total_operations] => 0 [finished_operations] => 0
And so on. No errors, but no operations!
Essentially, my code looks like this, where $emails stores all the emails in an array.
include("MailChimp.php");
include("Batch.php");
$list_id = "1234abcd";
use \DrewM\MailChimp\MailChimp;
use \DrewM\MailChimp\Batch;
$apiKey = 'aslkjf84983hg84938h89gd-us13';
if(!isset($emails)){ // If not sending bulk requests
$MailChimp = new MailChimp($apiKey);
$subscriber_hash = $MailChimp->subscriberHash($email);
$result = $MailChimp->patch("lists/$list_id/members/$subscriber_hash",
array(
'status' => 'subscribed',
)
);
/* SENDING BATCH OF EMAILS */
} else if($emails){
$MailChimp = new MailChimp($apiKey);
$Batch = $MailChimp->new_batch();
$i = 1;
foreach($emails as &$value){
$Batch->post("op".$i, "lists/$list_id/members", [
'email_address' => $value,
'status' => 'subscribed',
]);
$i++;
}
$result = $Batch->execute(); // Send the request (not working I guess)
$MailChimp->new_batch($batch_id); // Now get results
$result = $Batch->check_status();
print_r($result);
}
If anyone can see what I'm not seeing, I'll be very grateful!

Problem solved. After talking with a rep at MailChimp, he helped to find two major problems.
Instead of using a POST method, he said to use PUT, when working with already existing emails. POST is best used for adding emails, while PUT can add and update emails.
So, change
$Batch->post
to
$Batch->put
Secondly, after successfully sending requests and getting errors in the $result, he found they were 405 errors and told me to add the md5 hash to my emails.
So, change
$Batch->post("op".$i, "lists/$list_id/members", [ ...
to
$subscriber_hash = $MailChimp->subscriberHash($value);
$Batch->put("op$i", "lists/$list_id/members/$subscriber_hash", [ ...
And they sent me a MailChimp stocking cap for being a good sport :-)
Veni. Vidi. Vici.

Related

PHP - For each loop does not always do all iterations

Currently, I have the following problem:
I have created a WordPress environment that sends personalized emails to subscribers based on their preferences. This has worked for quite some time but for a couple of months, we are experiencing some inconsistencies. These inconsistencies are as followed:
Once in a while, the foreach loop for sending the emails stops in the middle of its execution. For example, we have a newsletter with 4000 subscribers. Once in a while, the program randomly stops its sending procedure at around 2500 emails. When this happens, there are literally no signs of any errors and there is also nothing to be seen in the debug log.
I have tried the following things to fix the issue:
Different sender; we switched from Sendgrid to SMTPeter (Dutch SMTP service)
Delays; we have tried whether placing a wait after x number of emails would have any impact because there might be too many requests per minute, but this was not the case.
Disable plugins; For 5 weeks we thought we had found the problem. WordFence seemed to be the problem, unfortunately, the send function stopped again last week and this did not appear to be causing the problems. Just to show how unstable it really is. It can go well for 5 weeks and then not for 2 weeks.
Rewriting of functions
Logging, we write values ​​to a txt file after every important step to keep track of where the send function stops. This is just to see which users have received an email and which still need to receive it so that we can continue sending it from there.
Debug log, the annoying thing is that even when we have the wp_debug on, nothing comes up that indicates a cause of crashing.
To schedule the sender I use the WP_Cron to run the task in the background. From there the following function is triggered;
Below, the code I wrote in stripped format. I removed all the $message additions as this is just HTML with some variables of ACF for the email. I translated it so it's easier to understand.
<?php
function send_email($edition_id, $post)
{
require_once('SMTPeter.php'); //Init SMTPeter Sender
$myfile = fopen("log.txt", "a") or die("Unable to open file!"); //Open custom logfile
$editionmeta = get_post_meta($edition_id); //Get data of edition
$users = get_users();
$args = array(
'post_type' => 'articles',
'post_status' => 'publish',
'posts_per_page' => -1,
'order' => 'asc',
'meta_key' => 'position',
'orderby' => 'meta_value_num',
'meta_query' => array(
array(
'key' => 'edition_id',
'value' => $edition_id,
'compare' => 'LIKE',
),
),
);
$all_articles = new WP_Query($args); // Get all articles of edition
$i = 0; // Counter users interrested in topic
$j = 0; // Counter sent emails
foreach ($users as $user) { //Loop over all users <---- This is the loop that not always finishes all itterations
$topic_ids = get_field('topicselect_', 'user_' . $user->ID);
$topic_id = $editionmeta['topic_id'][0];
if (in_array($editionmeta['topic_id'][0], $topic_ids)) { // Check if user is interrested in topic.
$i++; // Counter interrested in topic +1.
// Header info
$headerid = $editionmeta['header_id'][0];
$headerimage = get_field('header_image', $headerid);
$headerimagesmall = get_field('header_image_small', $headerid);
// Footer info
$footerid = $editionmeta['footer_id'][0];
$footer1 = get_field('footerblock_1', $footerid);
$footer2 = get_field('footerblock_2', $footerid);
$footer3 = get_field('footerblock_3', $footerid);
$message = '*HTML header newsletter*'; // First piece of content email
if ($all_articles->have_posts()) :
$articlecount = 0; // Set article count to check for empty newsletters
while ($all_articles->have_posts()) : $all_articles->the_post();
global $post;
$art_categories = get_the_category($post->ID); // Get categories of article
$user_categories = get_field('user_categories_', 'user_' . $user->ID); // Get categories user is interrested in
$user_cats = array();
foreach ($user_categories as $user_category) {
$user_cats[] = $user_category->name; // right format for comparison
}
$art_cats = array();
foreach ($art_categories as $art_category) {
$art_cats[] = $art_category->name; // right format for comparison
}
$catcheck = array_intersect($user_cats, $art_cats); // Check if 1 of the article's categories matches one of a user's categories
if (count($catcheck) > 0) { // The moment the array intersect count is greater than 0 (at least 1 category matches), the article is added to the newsletter.
$message .= "*Content of article*"; // Append article to content of newsletter
$articlecount++;
}
endwhile;
endif;
if ($articlecount > 0) { //As soon as the newsletter contains at least 1 article, it will be sent.
$j++; //Sent email counter.
$mailtitle = $editionmeta['mail_subject'][0]; // Title of the email
$sender = new SMTPeter("*API Key*"); // Class SMTPeter sender
$output = $sender->post("send", array(
'recipients' => $user->user_email, // The receiving email address
'subject' => $mailtitle, // MIME's subject
'from' => "*Sender*", // MIME's sending email address
'html' => $message,
'replyto' => "*Reply To*",
'trackclicks' => true,
'trackopens' => true,
'trackbounces' => true,
'tags' => array("$edition_id")
));
error_log(print_r($output, TRUE));
fwrite($myfile, print_r($output, true));
}
}
}
fclose($myfile);
}
All I want to know is the following;
Why can't my code run the foreach completely, every time? I mean, it's quite frustrating to see that it sometimes works like a charm, and the next time it could get stuck again.
Some things I thought about but did not yet implement:
Rewrite parts of the function into separate functions. Retrieving the content and setting up the HTML for the newsletter could be done in a different function. Besides the fact that it would obviously be an improvement for cleaner code, I just wonder if this could actually be the problem.
Can a foreach crash due to a fwrite trying to write to a file that is already being written to? So does our log cause the function to not run properly? (Concurrency, but is this a thing in PHP with its workers?)
Could the entire sending process be written in a different way?
Thanks in advance,
Really looking forward to your feedback and findings

GMAIL API - How to process attachments of email when looping through batch request results

"We support the Gmail REST API on Stack Overflow. Google engineers monitor and answer questions with the gmail-api tag.
You should use this tag when asking questions. We aim to answer all questions within a couple of days."
OK so after many frustrating days I found this on the Gmail api support page so here goes.
The short question:
How can I get the attachments for an specific email via the getData() method while looping through $emails from a batch request?
This would be so that when a user loads our inbox they can all the data required to view say 20 messages, instead of having to click on each one and wait for another call to be made which tends to take up to 6 seconds to process.
More Info
I'm trying to upgrade our platform which uses the Gmail API for our Gmail users to manage their inboxes from inside our platform.
I am trying to get all attachments for an email (inline or attached separately) to allow the user to read the email, with any inline images already visible on the page, and also be able to quickly preview any attachments or just download.
I have no issues getting everything I need for a single message.
But as soon as I try to use that code inside a batched request response, I cannot get the attachments out without creating new services and calls for each message.
This really slows everything down as it's making a new call for every email just to get the attachments.
Here's my code:
This is what I am using to get the attachment details out of the relevant part:
public function get_attachment_from_part($service, $emailId, $part) {
//spit_out($service);
//echo 'THIS IS THE FIRST PART:::';
//spit_out($part);
echo $attachmentId = $part->getBody()->getAttachmentId();
echo "<br/>".$filename = $part->getFilename();
echo "<br/>".$mimeType = $part->getMimeType();
echo "<br/>".$partId = $part->getPartId();
$attachment = new stdClass();
$attachment->filename = $filename;
$attachment->displayHeader = 'Content-Type: '.$mimeType;
$attachment->mimeType = $mimeType;
echo "<br/>".$attachment->messageId = $emailId;
$attachment->attachmentId = $attachmentId;
$attachment->partId = $partId;
$file = $service->users_messages_attachments->get('me', $emailId, $attachmentId);
spit_out($file);
$file = $file->getData(); // This fails because the response to $file has nothing in it.
$file = $this->inbox_new_model->decodeBody($file);
$attachment->file = $file;
return $attachment;
}
This is the response I get back when calling the attachments method:
Google_Http_Request Object
(
[batchHeaders:Google_Http_Request:private] => Array
(
[Content-Type] => application/http
[Content-Transfer-Encoding] => binary
[MIME-Version] => 1.0
)
[queryParams:protected] => Array
(
)
[requestMethod:protected] => GET
[requestHeaders:protected] => Array
(
[authorization] => Bearer ya29.Gl0WBEV_MbKok8PAG3YCBtlMI0hMnRsjJ7wCKWPJDAL9nFJlN7TuX8mJICDko-6PuCpsYiqaaVf-NVGzet9SIdPyJ9PZWTsEmlZVHdNOQXn8Kdc1NYOKU5_dNO4QiKU
)
[baseComponent:protected] => https://www.googleapis.com
[path:protected] => /gmail/v1/users/me/messages/15af343839f05824/attachments/ANGjdJ9FYdeQWlu1inYKKPZ6KQZ8G1hkjel1CuOMXKYI8eZNlRl2IjZ8oc_e9TqJ3Q_anX96afyxe6mr3jbZdGnQN0oy-C5uiMi5GMUC-Y_oQgqohgXkG1QcNukKZZ_rgRkqmqLy0O8zcEv2Y5WuOSEeHB9t6iDjmEgWSCy1VXHLmC-3XJVHlHyrCE6-tLHWPvaKf9gdvnuWhyl9XZJ_GdVpvhvJMs8Q5cPwk9jDwuQBw1unHbLqJeZxeGEBYNCH4ri-AuyV048E-3IhFOzfuW-5ST7fDQuhRsBzGKYZax3RDrNQT8mGV3qHXzoISLI
[postBody:protected] =>
[userAgent:protected] =>
[canGzip:protected] =>
[responseHttpCode:protected] =>
[responseHeaders:protected] =>
[responseBody:protected] =>
[expectedClass:protected] => Google_Service_Gmail_MessagePartBody
[accessKey] =>
)
as opposed to this when I use a single request per email:
Google_Service_Gmail_MessagePartBody Object
(
[attachmentId] =>
[data] => ## THE DATA RETURNED HERE IS THE CORRECT FILE AND I CAN DECODE AND USE IT
[size] => 8908
[modelData:protected] => Array
(
)
[processed:protected] => Array
(
)
)
Thanks in advance. Let me know if you need to see any other parts of the code.
Also to note, everything is retrieve successfully while looping through the batch response, it is only attachments which do not actually get returned in the following request and need to have a separate call made to get the actual attachment data:
$service->users_messages->get('me', $messageId, $optParamsGetMsg);

serverList() only returns 100 results

I'm trying to write a bit of code to inventory our OpenStack deployment, and I've run into an issue where serverList() only ever returns 100 results instead of the 600+ I'm expecting. I've reviewed the documentation and a bit of the source, and as far as I can tell there's no reason that this should be happening as the PaginatedIterator should be doing its pagination transparently.
There are no errors or warning either generated in my code, or logged on my controller [that I can find]. I am using php-opencloud v1.12 via composer.
use OpenCloud\OpenStack;
$client = new OpenStack('http://1.2.3.4:5000/v2.0/', array(
'username' => 'admin',
'password' => 'hunter2',
'tenantName'=> 'admin',
));
$service = $client->computeService('nova', 'RegionOne');
$stmt = $dbh->prepare('INSERT INTO servers VALUES (?,?)');
/* foreach($service->serverList() as $server) {
$stmt->execute([$server->id, $server->name]);
} // neither method works */
$list = $service->serverList();
while( $list->valid() ) {
$server = $list->current();
$stmt->execute([$server->id, $server->name]);
$list->next();
}
echo "\n";
var_dump($dbh->query('SELECT * FROM servers')->fetchAll(PDO::FETCH_ASSOC));
The default limit for pagination is 100. It is possible to override this with a higher limit like so:
$list = $service->serverList(null, array('limit' => 700));

Mail gun Cc header issue

Am using this function to sent bulk mail through mail gun
`//sending batch mail
public function BatchMail($subject=null,$body=null, $record=null)
{
# Instantiate the client.
$mg = new Mailgun(MAILGUN_KEY);
$domain = MAILGUN_DOMAIN;
# Next, instantiate a Message Builder object from the SDK, pass in your sending domain
if(!empty($record))
{
$batchMsg = $mg->BatchMessage($domain);
# Define the from address.
$batchMsg->setFromAddress(FROM_EMAIL, array("first"=>FIRST_NAME, "last" => LAST_NAME));
# Define the subject.
$batchMsg->setSubject($subject);
# Define the body of the message.
$batchMsg->setHtmlBody($body);
# Next, let's add a few recipients to the batch job.
foreach ($record as $key => $rec) {
$batchMsg->addToRecipient($rec['email'], array("first" => $rec['fname'], "last" => $rec['lname']));
}
$batchMsg->addCcRecipient("mayname#mycompany.in", array("first" => "Sally", "last" => "Doe"));
$re = $batchMsg->finalize();
$result = $batchMsg->getMessage();
$log = $mg->get("$domain/log");
$respbody = $log->http_response_body;
$result = $mg->get("$domain/stats",array('event' => array('sent', 'opened')));
$resp = $log->http_response_code;
//$event = $mg->get("$domain/events");
$response = array("log"=>$log,"result"=>$result,"body"=>$respbody,"code"=>$resp);
return $response;
}
}`
Here mails are sending properly but I have a problem in my cc mail.
$batchMsg->addCcRecipient("mayname#mycompany.in", array("first" => "Sally", "last" => "Doe"));
This function using for adding CC mails. Mails are delivering properly but the mail reviving with headers like To: mayname#mycompany.in, Cc:mayname#mycompany.in. But the recipients mails are not listing in the headers.
Normally gamil showing recipients in the headers like this
to: test#gmail.com
cc: myname#mycompany.in
Anyone know why mail gun showing issue like this ??
Replace:
foreach ($record as $key => $rec) {
$batchMsg->addToRecipient($rec['email'], array("first" => $rec['fname'], "last" => $rec['lname']));
}
With:
foreach ($record as $key => $rec) {
$batchMsg->addBccRecipient($rec['email'], array("first" => $rec['fname'], "last" => $rec['lname']));
}
Or if you want your recipients to know who else was emailed don't use the batch message builder. See the "To" parameter in the mailgun API documentation for proper formatting.
The batch builder is for sending many distinct emails and not for composing a single email to many different recipients. It's possible that MailGun didn't anticipate you building an email to multiple recipients this way and thus there is no test coverage of this bug. You might try filing a bug report on their github page.
More details about the specification you're trying to implement might lead to a better answer.

Codeigniter/PHP: Creating users in batch with TankAuth

I am interested in creating bulk users/passwords in a batch using TankAuth for CodeIgniter. I asked this question on the CI forums but got no responses:
http://codeigniter.com/forums/viewthread/110993/P330/#837327
Google searches don't turn up much besides my thread as the third result and a bunch of unrelated sites.
http://www.google.com/search?sourceid=chrome&ie=UTF-8&q=create+batch+users+tankauth
Has anyone successfully done this using a recursion algorithm? If so, can you post some code to get me going on the right path? Thanks!
Versions of software:
CI 1.7.3
TankAuth 1.0.7
PHP 5.x
EDIT 2/15:
Just in case anyone is looking for a solution to this, here's a function that is basically the same one I used (there were some other parameters, but this should get you going):
function batchReg()
{
$this->load->model('mymodel');
// connect to the database
$this->mymodel->dbconnect();
// build it
$query = "SELECT user, email, pass from newusers ORDER BY user ASC";
// ship it
$result = mysql_query($query);
// loop it
while ($row = mysql_fetch_array($result))
{
$data = $this->tank_auth->create_user($row['user'], $row['email'], $row['pass'], FALSE);
print_r($data);
echo "<p>";
}
}
Just ping batchReg() from the controller to put it into action!
Sounds like a simple loop operation to me
somehow (whatever the source) get your usernames in a iterable form like an array $user_list
we'll say it looks like this
Array(
Array(
[username] => '...',
[email] => '...',
[password] => '', //leave password empty
),
Array(
[username] => '...',
[email] => '...',
[password] => '', //leave password empty
),
... etc.
)
Then create a simple looping routine to process the new registrations, storing the password back into the array, so you will then have a complete list of logins, new (randomized) passwords and emails.
//loop by referance in order to properly store the generated password
foreach($user_list as &$user) {
//generate 8 char password and store it
$user['password'] = substr(uniqid(),2,8);
//run register routine (not sure on tank auth's specific syntax
$this->tankauth->register($user['username'],$user['email'],$user['password'],FALSE);
}
Then at the end your $user_list contains all the new passwords for your users.

Categories