I'm trying to work out how I can use WWW::Mailchimp ( http://search.cpan.org/~arcanez/WWW-Mailchimp/ ) to sign someone up to our list, but also assign the language of the person (i.e english, french, german, spanish, etc).
Here is what I have thus far:
my $mailchimp = WWW::Mailchimp->new(apikey => 'xxxx' );
$mailchimp->listSubscribe( id => "xxx", email_address => $in->{Email}, merge_vars => [ FNAME => $name[0], LNAME => $name[1], mc_language => "fr", LANG => "fr", LANGUAGE => "fr" ] );
mc_language => "fr", LANG => "fr", LANGUAGE => "fr" doesn't seem to do anything (been trying all the params I see laying around, in the vain hope one of them works!)
While it works (and asks you to confirm your subscription), all the language variables are ignored. Looking at their documents, I'm a bit confused as to what to use:
https://apidocs.mailchimp.com/api/2.0/lists/subscribe.php
The code "fr" is ok, but I'm unsure what params to pass along to it.
Has anyone had any experience with this before? Apart from the language, it works fine (but I need to be able to send the confirmation emails in their own language, and then also filter down when doing mailings)
UPDATE: Ok, so it looks like its not going to be a simple case of updating to the newer API. I've been looking into the v3.0 API, and its a total overhaul of the older one (new function names, new ways of sending requests, etc). What I'm going to do is look into a "Curl" method, so we can at least get it going with that. Once I've got that going, I'll probably have a look at coding something to work with LWP::UserAgent, as that'd be cleaner than doing lots of curl requests. Shame there isn't anything out there already for Perl and MailChimp (with the new API, or even version 2.0!)
From looking at the source, it defaults to API 1.3:
has api_version => (
is => 'ro',
isa => Num,
lazy => 1,
default => sub { 1.3 },
);
The documentation for that shows you need to use MC_LANGUAGE:
string MC_LANGUAGE Set the member's language preference. Supported
codes are fully case-sensitive and can be found here.
It looks like the module just shoves whatever data structure you provide into JSON and POSTs it to Mailchimp, so the appropriate Mailchimp API doc version for the API you target should be referenced as a primary source.
Ok, so I got there in the end! I have been talking with MailChimp support, and they were very helpful. Turns out it was a double issue.
1) Auto-Translate needed to be enabled for the list in question. This was their answer around that:
After taking a look at the call, it appears to be set up properly now, so you are all good on that front. That being said, I am seeing
that the Auto-translate option doesn't seem to be enabled for any of
your lists. In order for the Confirmation and all other response
emails to automatically translate, this will need to be enabled for
all of the lists being used.
We have a bit of additional information on that, here, if you'd like to check that out:
http://kb.mailchimp.com/lists/signup-forms/translate-signup-forms-and-emails#Auto-Translate-Forms
2) When making the request via the API, you need to specifically set the Accept-Language: xx value. For example, en, fr, es, de, etc.
Here is a working function for anyone who needs it in the future. Just be sure to update the apikey,listId and endpoint URL.
do_register_email_list('foo#bar.com','Andrew Test',"en")
sub do_register_email_list {
# (email,name,lang)
use WWW::Curl::Easy;
use Digest::MD5;
use JSON;
my #name = split /\s+/, $_[1];
my $apikey = 'xxxx-us6';
my $listid = 'xxxx';
my $email = $_[0];
my $endpoint = "https://us6.api.mailchimp.com/3.0/lists";
my $lang = $_[2]||'en';
my $json = JSON::encode_json({
'email_address' => $email,
'status' => 'pending',
'language' => $lang,
'merge_fields' => {
'FNAME' => $name[0]||'',
'LNAME' => $name[1]||''
}
});
my $curl = WWW::Curl::Easy->new;
my $url = "$endpoint/$listid/members/" . Digest::MD5::md5(lc($email));
$curl->setopt(CURLOPT_HEADER,1);
$curl->setopt(CURLOPT_URL, $url);
# $curl->setopt(CURLOPT_VERBOSE, 1);
$curl->setopt(CURLOPT_USERPWD, 'user:' . $apikey);
$curl->setopt(CURLOPT_HTTPHEADER, ['Content-Type: application/json',"Accept-Language: $lang"]);
$curl->setopt(CURLOPT_TIMEOUT, 10);
$curl->setopt(CURLOPT_CUSTOMREQUEST, 'PUT');
$curl->setopt(CURLOPT_SSL_VERIFYPEER, 0);
$curl->setopt(CURLOPT_POSTFIELDS, $json);
# A filehandle, reference to a scalar or reference to a typeglob can be used here.
my $response_body;
$curl->setopt(CURLOPT_WRITEDATA,\$response_body);
# Starts the actual request
my $retcode = $curl->perform;
#print "FOO HERE";
# Looking at the results...
if ($retcode == 0) {
print "Transfer went ok\n";
my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE);
print "Received response: $response_body\n";
} else {
# Error code, type of error, error message
print "An error happened: $retcode ".$curl->strerror($retcode)." ".$curl->errbuf."\n";
}
}
Hopefully this saves someone else from all the grief I had with it :) (the MailChimp support lady also said she will get their team to add something about this in the developer notes, so its made a bit clearer!)
Related
I have a Perl script that pulls a table from a SQL database ($row variable) and attempts to do a MongoDB update like so:
my $res = $users->update({"meeting_id" => $row[0]},
{'$set' => {
"meeting_id" => $row[0],
"case_id" => $row[1],
"case_desc" => $row[2],
"date" => $row[3],
"start_time" => $row[4],
"end_time" => $row[5],
#"mediator_LawyerID" => $row[6],
"mediator_LawyerIDs" => \#medLawIds,
"case_number" => $row[6],
"case_name" => $row[7],
"location" => $row[8],
"number_of_parties" => $row[9],
"case_manager" => $row[10],
"last_updated" => $row[11],
"meeting_result" => $row[12],
"parties" => \#partyList
}},
{'upsert' => 1}) or die "I ain't update!!!";
My client now wants ICS style calendar invites sent to their mediators. Thus, I need to know whether an update or insert happened. The documentation for MongoDB::UpdateResult implies that this is how you access such a property:
my $id = $res->upserted_id;
So I tried:
bless ($res,"MongoDB::UpdateResult");
my $id = $res->upserted_id;
After this code $id is like:
HASH(0x356f8fc)
Are these the actual IDs? If so, how do I convert to a hexadecimal string that can be cast to Mongo's ObjectId type? It should be noted I know absolutely nothing about perl; if more of the code is relevant, at request I will post any section ASAP. Its 300 lines so I didn't want to include the whole file off the bat.
EDIT: I should mention before anyone suggests this that using update_one instead of update returns the exact same result.
HASH(0x356f8fc) is a Perl Hash reference. It's basically some kind of (internal) memory address of some data.
The easiest way to get the contents is Data::Dumper:
use Data::Dumper
[...]
my $result = $res->upserted_id;
print Dumper($result);
HASH(0x356f8fc) is just the human readable representation of the real pointer. You must dump it in the same process and can't pass it from one to another.
You'll probably end up with something like
`my $id = $result->{_id};`
See the PerlRef manpage for details.
See also the MongoDB documentation about write concern.
PS: Also remember that you could use your own IDs for MongoDB. You don't need to work with the generated ones.
I'm new in this forum and I'm having some problems with the perl library Net::Twitter:Stream. I'm following the example in this link Net::Twitter:Stream.
But it is missing the part when I get a bad response code(another than 200) and I have to stop my algorithm. So, what can I do in this case? I'm afraid to use it so much and enter into the twitter black list...
I'm basing in this code below:
use Net::Twitter::Stream;
Net::Twitter::Stream->new ( user => $username, pass => $password,
callback => \&got_tweet,
track => 'perl,tinychat,emacs',
follow => '27712481,14252288,972651' );
sub got_tweet {
my ( $tweet, $json ) = #_; # a hash containing the tweet
# and the original json
print "By: $tweet->{user}{screen_name}\n";
print "Message: $tweet->{text}\n";
}
I think you'll want to add connection_closed_cb=>\&bad_response, see this stackoverflow questions last answer. I'm not sure why that ability isn't documented but it is available if you check the source code. I also couldn't find that module in CPAN.
I'm trying to submit a form by post method using WWW::Mechanize perl module.
use WWW::Mechanize;
my $mech = WWW::Mechanize->new();
...
$mech->get($url);
...
my $response = $mech->submit_form(
form_name => $name,
fields => {
$field_name => $field_value
},
button => 'Button'
);
$field_name is generally speaking a text field (though the type is not specified explicitly in the form), which has a preset value.
$field_name => $field_value in $mech->submit_form on whatever reason does not replace the value, instead $field_value is added into the form after the original value:
{submitted_field_value} = {original_value},{provided_value}
How to replace {original_value} with {provided_value} in the form to be submitted ?
What happens if you add this single line to your code before calling $mech->submit_form():
$mech->field( $name, [$field_value], 1 );
This makes sure that the first value is added, or overwritten if it already exists.
1 is the number parameter (or position index)
See the documentation of WWW::Mechanize:
$mech->field( $name, \#values, $number )
Given the name of a field, set its value to the value specified. [...]
The optional $number parameter is used to distinguish between two
fields with the same name. The fields are numbered from 1.
It's important to remember WWW::Mechanize is better thought of as a 'headless browser' as opposed to say LWP or curl, which only handle all the fiddly bits of http requests for you. Mech keeps its state as you do things.
You'll need to get the form by using $mech->forms or something similar (its best to decide from the documentation. I mean there so many ways to do it.), and then set the input field you want to change, using the field methods.
I guess the basic way to do this comes out as so:
$mech->form_name($name);
$mech->field($field_name, $field_value);
my $response = $mech->click('Button');
Should work. I believe it will also work if you get the field and directly use that (ie my $field = $mech->form_name($name); then use $field methods instead of $mech.
I managed to make it working at my will. Thanks Timbus and knb for your suggestions. Though my case may not be completely general (I know the preset value) but I'd share what I've found (by trails & errors).
my $mech = WWW::Mechanize->new();
$mech->get($url);
$mech->form_name( $name );
my $fields = $mech->form_name($name);
foreach my $k ( #{$fields->{inputs}}){
if ($k->{value} eq $default_value){
$k->{value}=$field_value;
}
}
my $response = $mech->click('Button_name');
In this tutorial he creates a custom login form, just to show how it is done. Please search for
How do I remove the custom login and fall back to the default?
To code looks like this
sub cgiapp_init {
my $self = shift;
my %CFG = $self->cfg;
# ...
$self->authen->config(
DRIVER => [ 'Authen::Simple::LDAP',
host => '',
basedn => '',
],
STORE => 'Session',
LOGOUT_RUNMODE => 'logout',
LOGIN_RUNMODE => 'login',
POST_LOGIN_RUNMODE => 'okay',
RENDER_LOGIN => \&my_login_form,
);
$self->authen->protected_runmodes(
'mustlogin',
);
}
sub login : Runmode {
my $self = shift;
my $url = $self->query->url;
my $user = $self->authen->username;
if ($user) {
my $message = "User $user is already logged in!";
my $template = $self->load_tmpl('default.html');
$template->param(MESSAGE => $message);
$template->param(MYURL => $url);
return $template->output;
} else {
my $url = $self->query->self_url;
unless ($url =~ /^https/) {
$url =~ s/^http/https/;
return $self->redirect($url);
}
return $self->my_login_form;
}
}
Update
Here is mentions that CGI::Application have a default login that looks better than his.
Line 159 specifies a subroutine to use
to generate a login form. Note that
the Authentication plugin comes with a
default form that you can use. I'm
including this one just to demonstrate
how to go about creating one of your
own, in case you really want to. The
default one actually looks much better
than mine, so you might wish to
comment out line 159!
I'm the author of that tutorial. Sorry for the confusion!
What I should have said is "comment out lines 157, 158, and 159 of Login.pm".
To use the default form that's built in to the CGI::Application::Plugin::Authentication module, you don't need to specify LOGIN_RUNMODE, POST_LOGIN_RUNMODE, or RENDER_LOGIN.
Those are all provided just to help you customize your login page. I included a customized
version in the tutorial thinking that most people would need to know how to do so.
The default one actually looks much better than mine, so you might wish to comment out line 159!
Comment out line 159.
I am trying to write a Perl script to connect to me YouTube account but it doesnt seem to work. Basically I just want to connect to my account but apparently it is not working. I don't even have an idea on how I could debug this! Maybe it is something related to https protocol?
Please enlighten me! Thanks in advance.
use HTTP::Request::Common;
use LWP::UserAgent;
use strict;
my $login="test";
my $pass = "test";
my $res = "";
my $ua = "";
# Create user agent, make it look like FireFox and store cookies
$ua = LWP::UserAgent->new;
$ua->agent("Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20051213 Firefox/1.0.7");
$ua->cookie_jar ( {} );
# Request login page
$res = $ua->request(GET "https://www.google.com/accounts/ServiceLogin?service=youtube&hl=en_US&passive=true<mpl=sso&uilel=3&continue=http%3A//www.youtube.com/signup%3Fhl%3Den_US%26warned%3D%26nomobiletemp%3D1%26next%3D/index");
die("ERROR1: GET http://www.youtube.com/login\n") unless ($res->is_success);
# Now we login with our user/pass
$res = $ua->request(
POST "https://www.google.com/accounts/ServiceLoginAuth?service=youtube",
Referer => "http://www.youtube.com/login",
Content_Type => "application/x-www-form-urlencoded",
Content => [
currentform => "login",
next => "/index",
username => $login,
password => $pass,
action_login => "Log+In"
]
);
# YouTube redirects (302) to a new page when login is success
# and returns OK (200) if the login failed.
#die("ERROR: Login Failed\n") unless ($res->is_redirect());
print $res->content;
what i am doing is learning the web features of perl, so i dont want to use any library except wwwlib or mechanize to get the job done.
how can i just connect to my account using a perl script? this is my objective for now
hope someone can post a script or correct mine.
thank you guys for you help.
i am testing Webscarab now..
What data are you trying to grab? Why not just using an existing implementation like WebService::YouTube
Some comments on your code: I always avoided the shortcut $ua->request(GET/POST) method since I always ended up needing more flexibility that only the use of HTTP::Request and HTTP::Response allowed. I always felt the code was cleaner that way too.
Why is your code not working? Who knows.
Make sure your cookiejar is adding your cookies to the outgoing HTTP::Request. I'd suggest dumping all your headers when you do it in a browser and compare with the headers and data that libwww is sending. There may be some additional fields that they are checking for that vary for every hit. They may be checking for your UserAgent string. If you are just looking to learn libwww I'd suggest using a different site as a target as I'm sure YouTube has all sort of anti-scripting hardening.
Are you using YouTube's stable documented API?
Use an HTTP proxy such as WebScarab to watch the data flow.
Trey's suggestion to use somebody else's CPAN package for the mechanics is a good idea too.
Right right by and large, what you want to do is define a cookiejar for most of these websites that have a redirection login. This is what the package has done. Also the package tunes a lot of the lookups and scrapes based on the youtube spec.
Ajax content for example will be rough since its not there when your scraping
You just picked a somewhat rough page to start out with.
Enjoy
I'm actually working on this issue myself. Before, I would suggest read over this the API guide from Google as a good starting reference. If I'm reading it correctly, one begins with passing user credentials through a REST interface to get a Authentication Token. To handle that, I'm using the following:
sub getToken {
my %parms = #_;
my $response = LWP::UserAgent->new->post(
'https://www.google.com/youtube/accounts/ClientLogin',
[
Email => $parms{'username'},
Passwd => $parms{'password'},
service => "youtube",
source => "<<Your Value Here>>",
]
);
my $content = $response->content;
my ($auth) = $content =~ /^Auth=(.*)YouTubeUser(.*)$/msg
or die "Unable to authenticate?\n";
my ($user) = $content =~ /YouTubeUser=(.*)$/msg
or die "Could not extract user name from response string. ";
return ($auth, $user);
}
And I call that from the main part of my program as such:
## Get $AuthToken
my ($AuthToken, $GoogleUserName) = getToken((
username => $email, password => $password
));
Once I have these two things -- $AuthToken and $GoogleUserName, I'm still testing the LWP Post. I'm still writing this unit:
sub test {
my %parms = #_;
## Copy file contents. Use, foy's three param open method.
my $fileSize = -s $parms{'File'};
open(VideoFile, '<', "$parms{'File'}") or die "Can't open $parms{'File'}.";
binmode VideoFile;
read(VideoFile, my $fileContents, $fileSize) or die "Can't read $parms{'File'}";
close VideoFile;
my $r = LWP::UserAgent->new->post(
"http://uploads.gdata.youtube.com/feeds/api/users/$parms{'user'}/uploads",
[
Host => "uploads.gdata.youtube.com",
'Authorization' => "AuthSub token=\"$parms{'auth'}\"",
'GData-Version' => "2",
'X-GData-Key' => "key=$YouTubeDeveloperKey",
'Slug' => "$parms{'File'}",
'Content-Type' => "multipart/related; boundary=\"<boundary_string>\"",
'Content-Length' => "<content_length>",
'video_content_type'=> "video/wmv",
'Connection' => "close",
'Content' => $fileContents
]
);
print Dumper(\$r->content)
}
And that is called as
&test((auth=>$Auth, user=>$user, File=>'test.wmv'));