How can I read messages in a Gmail account from Perl? - perl

I have used the module Mail::Webmail::Gmail to read the new messages in my Gmail account.
I have written the following code for this purpose:
use strict;
use warnings;
use Data::Dumper;
use Mail::Webmail::Gmail;
my $gmail = Mail::Webmail::Gmail->new(
username => 'username', password => 'password',
);
my $messages = $gmail->get_messages( label => $Mail::Webmail::Gmail::FOLDERS{ 'INBOX' } );
foreach ( #{ $messages } ) {
if ( $_->{ 'new' } ) {
print "Subject: " . $_->{ 'subject' } . " / Blurb: " . $_->{ 'blurb' } . "\n";
}
}
But it didn't print anything.
Can anyone help me in this or suggest any other module for this?
Thanks in advance.

This is taken almost word from word from the Net::IMAP::Simple POD:
use strict;
use warnings;
# required modules
use Net::IMAP::Simple;
use Email::Simple;
use IO::Socket::SSL;
# fill in your details here
my $username = 'user#example.com';
my $password = 'secret';
my $mailhost = 'pop.gmail.com';
# Connect
my $imap = Net::IMAP::Simple->new(
$mailhost,
port => 993,
use_ssl => 1,
) || die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n";
# Log in
if ( !$imap->login( $username, $password ) ) {
print STDERR "Login failed: " . $imap->errstr . "\n";
exit(64);
}
# Look in the INBOX
my $nm = $imap->select('INBOX');
# How many messages are there?
my ($unseen, $recent, $num_messages) = $imap->status();
print "unseen: $unseen, recent: $recent, total: $num_messages\n\n";
## Iterate through unseen messages
for ( my $i = 1 ; $i <= $nm ; $i++ ) {
if ( $imap->seen($i) ) {
next;
}
else {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject') );
}
}
# Disconnect
$imap->quit;
exit;

You can use the Mail::POP3Client module. It is used to get the message from the Gmail account.

Have you tried doing some error checking with after you try an operation
if ($gmail->error())
{
print $gmail->error_msg();
}
I found that when I do it it results in:
Error: Could not login with those
credentials - could not find final URL
Additionally, HTTP error: 200 OK
Error: Could not Login.
I believe it may be because this module was last updated in 2006 and gmail may have changed the way the logins work so it may no longer be able to access it.
What you could do if you don't just want to download new messages with pop3 is you can use
Net::IMAP::Simple to access a gmail account via IMAP

Related

print out email on terminal using data::dumper

I am not understanding how to use Data::Dumper even after reading the Perl doc and looking at other scripts in git. I see lots of examples online dealing with hashes, but I didn't think that quite fit with what I need to do.
I am creating a script to send emails to managers or teams regarding terminated employees. I was told to add print Dumper $email to my code so that when --dry_run option is used, we could see on the terminal a printout of what the email would look like. --dry_run would also ensure that the email isn't actually sent. When I run perl <script> --dry_run, nothing happens. Maybe I need to do something along the lines of $d = Data::Dumper->new(?
Here is a snippet of my code:
#!/usr/bin/perl
use strict;
use warnings;
use NIE::Email;
use Data::Dumper;
use List::Util qw(any);
use Getopt::Long;
Getopt::Long::Configure qw(gnu_getopt);
my ($qa, $verbose, $dry_run, $help, $dbh);
GetOptions(
'qa' => \$qa,
'verbose|v' => \$verbose,
'dry_run' => \$dry_run,
'help|h' => \$help
);
#Generate email here
sub mail_func {
print "Prepare email\n" if $verbose;
my $n = shift; #user
my $i = shift; #ips
my $t = shift; #testnets
my $m = shift; #managers (multiple if owner is undef)
my #to_list; # send to field
foreach my $value (values %{$t}) {
if ($value ne 'lab#abc.com') { #don't send this email to lab#
if (any { $value eq $_ } #to_list) { #check not already listed
next;
}
else { push(#to_list, $value); }
}
}
foreach my $key (keys %{$m}) {
if ($key ne 'def') {
if (any { $key eq $_ } #to_list) {
next;
}
else { push(#to_list, $key . '#abc.com'); }
}
}
my #body;
while (my ($key, $value) = each %{$i}) {
my $b = "IP " . $key . " : Testnet " . $value . "\n";
push(#body, $b);
}
my $sub1 = "Ownership needed!";
my $sub2 = "Ownership needed script special case";
my $email;
#Email testnet group (if not lab) as well as manager of term employee
if (#to_list) {
$email = NIE::Email->new(
From => 'do-not-reply#abc.com',
To => join(',', #to_list),
'Reply-to' => 'def#abc.com',
Subject => $sub1,
);
$email->data(
"Good Day, \n\n The below machines need claimed as their previous"
. " owner, $n, is showing up as no longer with the company. \n"
. "Please visit website to change"
. " ownership of these machhines. \n\n"
. "#body \n\n"
. "If you have already requested an ownership change for these"
. " machines, please disregard this message."
. "\n\n Thank you \n -Lab team \n\n"
. "This script is under active development and could contain"
. " bugs, so please speak up if you have doubts or something "
. "looks strange."
. "\n Script name: lab_ownership_needed_email");
if ($dry_run) {print Dumper($email);}
else {$email->send();}
}
Any help in understanding how to use this for my purpose would be greatly appreciated. Thank you.
Reverted to original, re-added in code, re-ran the script, and it works.
The above code is correct as is.
Thanks to simbabque who stated the code looked correct in the first place.

Perl Getopt::Declare parameter action not invoked

I use Getopt::Declare in a script but invoking the script and passing -get_ip "test" doesn't do anything i.e. the script executes the "my" statements and getFirstAvailableIP doesn't get called.
use Getopt::Declare;
use lib "/home/vtsingaras/NicTool/client/lib/";
use NicToolServerAPI;
use strict;
use warnings;
#debug remove
use Data::Dumper;
#NicToolServer settings, edit
my $ntconf = {
ntuser => 'censored',
ntpass => 'censored',
nthost => 'censored',
ntport => 8082,
};
my ( $zone, $fqdn, $ip, $comment );
my $options_spec = q(+g[et_ip] <zone> Get the first available IP from the provided reverse <zone>.
{getFirstAvailableIP($::zone);}
+s[et_dns] <fqdn> <ip> <comment> Create an A record for <fqdn> that points to <ip> and the associated PTR record.
{createFwdAndPtr($::fqdn, $::ip, $::comment);}
);
my $args = Getopt::Declare->new($options_spec);
#Setup NicTool
my $nt = new NicToolServerAPI;
$NicToolServerAPI::server_host = $ntconf->{nthost};
$NicToolServerAPI::server_port = $ntconf->{ntport};
$NicToolServerAPI::data_protocol = "soap";
#$NicToolServerAPI::use_https_authentication = 0;
sub nt_login {
#Login to NicTool Server
my $ntuser = $nt->send_request(
action => "login",
username => $ntconf->{ntuser},
password => $ntconf->{ntpass},
);
if ( $ntuser->{error_code} ) {
print( "Unable to log in: " . $ntuser->{error_code} . " " . $ntuser->{error_msg} . "\n" );
exit 1;
} else {
print( "Logged in as " . $ntuser->{first_name} . " " . $ntuser->{last_name} . "\n" );
}
}
sub getFirstAvailableIP {
my $fqdn = $_[0];
print $fqdn;
die "blah";
}
The problem is that you specified + instead of - in $options_spec for get_ip.
Here is a self-contained runnable example which calls getFirstAvailableIP:
use strict;
use warnings;
use Getopt::Declare;
my $zone;
my $args = Getopt::Declare->new(<<'END_OPTS');
# tab
# ||||
# vvvv
-g[et_ip] <zone> Get the first available IP from the provided reverse <zone>.
{ getFirstAvailableIP($zone); }
END_OPTS
print "hello world\n";
exit;
sub getFirstAvailableIP {
print "blah - #_\n";
}
__END__
And executed:
$ perl declare_test.pl -get_ip test
blah - test
hello world
Note that this module requires a tab character in its specification; this makes it difficult to copy'n'paste correctly.

How to check if last email sent was successfully delivered or not using MIME::Lite perl

I wanted to send emails in perl code. So I used MIME::Lite module.
I am able to send emails as I wanted if I removed last_send_successful check, else I get error mentioned below.I want to know if the email was sent successfully. Below is the code snippet I used.
sub sendEmailWithCSVAttachments {
my $retries = 3;
my $retry_duration = 500000; # in microseconds
my $return_status;
my ( $from, $to, $cc, $subject, $body, #attachments_path_array );
$from = shift;
$to = shift;
$cc = shift;
$subject = shift;
$body = shift;
#attachments_path_array = shift;
my $msg = MIME::Lite->new(
From => $from,
To => $to,
Cc => $cc,
Subject => $subject,
Type => 'multipart/mixed'
) or die "Error while creating multipart container for email: $!\n";
$msg->attach(
Type => 'text',
Data => $body
) or die "Error while adding text message part to email: $!\n";
foreach my $file_path (#attachments_path_array) {
my $file_name = basename($file_path);
$msg->attach(
Type => 'text/csv',
Path => $file_path,
Filename => $file_name,
Disposition => 'attachment'
) or die "Error while adding attachment $file_name to email: $!\n";
}
my $sent = 0;
while ( !$sent && $retries-- > 0 ) {
eval { $msg->send(); };
if ( !$# && $msg->last_send_successful() ) {
$sent = 1;
} else {
print "Sending failed to $to.";
print "Will retry after $retry_duration microseconds.";
print "Number of retries remaining $retries";
usleep($retry_duration);
print "Retrying...";
}
}
if ($sent) {
my $sent_message = $msg->as_string();
print "Email sent successfully:";
print "$sent_message\n";
$return_status = 'success';
} else {
print "Email sending failed: $#";
$return_status = 'failure';
}
}
The error I am getting is:
Can't locate object method "last_send_successful" via package "MIME::Lite"
This means this method is not present. But it is given in the reference I am using.
So am I missing something or is there alternative to check if the last send was successful or the reference I am using is incorrect?
Is this check redundant as I am already using eval block?
Will using eval catch the error of email not getting delivered? (Most probably no but want to confirm)
How to make sure that email is delivered with MIME::Lite?
You don't need to use the eval block or do anything fancy to ensure that the mail has been sent; that is what last_send_successful is for. When the send subroutine successfully completes its work, it sets an internal variable ($object->{last_send_successful}); this is what the last_send_successful sub is checking. It is usually not necessary to use an eval block unless you are trying to prevent a script dying or throwing a runtime or syntax error.
You can simplify your code to something like the following:
$msg->send;
if ($msg->last_send_successful) {
# woohoo! Message sent
}
else {
# message did not send.
# take appropriate action
}
or
$msg->send;
while (! $msg->last_send_successful) {
# message did not send.
# take appropriate action
}

How can I read the body of a messages in a Gmail account from Perl?

I have readed what was posted 3 years ago here -> How can I read messages in a Gmail account from Perl?
But i can't open the body .. i've readed Net::IMAP::Simple and Email::Simple; . I'm trying this.. but doesn't works, it prints de from and the subject, but not the body.
use strict;
use warnings;
# required modules
use Net::IMAP::Simple;
use Email::Simple;
use IO::Socket::SSL;
# fill in your details here
my $username = 'email#gmail.com';
my $password = 'pass';
my $mailhost = 'imap.gmail.com';
# Connect
my $imap = Net::IMAP::Simple->new(
$mailhost,
port => 993,
use_ssl => 1,
) || die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n";
# Log in
if ( !$imap->login( $username, $password ) ) {
print STDERR "Login failed: " . $imap->errstr . "\n";
exit(64);
}
# Look in the INBOX
my $nm = $imap->select('INBOX');
# How many messages are there?
my ($unseen, $recent, $num_messages) = $imap->status();
print "unseen: $unseen, recent: $recent, total: $num_messages\n\n";
## Iterate through unseen messages
for ( my $i = 1 ; $i <= $nm ; $i++ ) {
if ( $imap->seen($i) ) {
next;
}
else {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
my $text = $es->body;
printf( "[%03d] %s\n\t%s\n%s", $i, $es->header('From'), $es->header('Subject'),$text);
}
}
# Disconnect
$imap->quit;
exit;
This print:
[001] <example#example.com>
test subject
Not the body of the email.
Can anyone solve this???
Thanks in advance.
The issue is that you are using:
$imap->top($i)
From the documentation (emphasis mine):
This method accepts a message number as its required parameter. That
message will be retrieved from the currently selected folder. On
success this method returns a list reference containing the lines of
the header. Nothing is returned on failure and the errstr() error
handler is set with the error message.
top doesn't return the body of the message. You'll need to use get for that. Something like this:
my $es = Email::Simple->new( join '', #{ $imap->get($i) } );
^^^

How to use Net::Twitter::Stream to read stream from API?

I'm trying to use the Net::Twitter::Stream Perl module from CPAN to read the stream from sample.json. I believe this is the corect module though they way they crafted it allows one to process the filter stream. I've modified it as such but I must be missing something as I don't get any data in return. I establish a connection but nothing comes back. I'm guessing this should be an easy fix but I'm a touch new to this part of Perl.....
package Net::Twitter::Stream;
use strict;
use warnings;
use IO::Socket;
use MIME::Base64;
use JSON;
use IO::Socket::SSL;
use LibNewsStand qw(%cf);
use utf8;
our $VERSION = '0.27';
1;
=head1 NAME
Using Twitter streaming api.
=head1 SYNOPSIS
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";
}
=head1 DESCRIPTION
The Streaming verson of the Twitter API allows near-realtime access to
various subsets of Twitter public statuses.
The /1/status/filter.json api call can be use to track up to 200 keywords
and to follow 200 users.
HTTP Basic authentication is supported (no OAuth yet) so you will need
a twitter account to connect.
JSON format is only supported. Twitter may depreciate XML.
More details at: http://dev.twitter.com/pages/streaming_api
Options
user, pass: required, twitter account user/password
callback: required, a subroutine called on each received tweet
perl#redmond5.com
#martinredmond
=head1 UPDATES
https fix: iwan standley <iwan#slebog.net>
=cut
sub new {
my $class = shift;
my %args = #_;
die "Usage: Net::Twitter::Stream->new ( user => 'user', pass => 'pass', callback => \&got_tweet_cb )" unless
$args{user} && $args{pass} && $args{callback};
my $self = bless {};
$self->{user} = $args{user};
$self->{pass} = $args{pass};
$self->{got_tweet} = $args{callback};
$self->{connection_closed} = $args{connection_closed_cb} if
$args{connection_closed_cb};
my $content = "follow=$args{follow}" if $args{follow};
$content = "track=$args{track}" if $args{track};
$content = "follow=$args{follow}&track=$args{track}\r\n" if $args{track} && $args{follow};
my $auth = encode_base64 ( "$args{user}:$args{pass}" );
chomp $auth;
my $cl = length $content;
my $req = <<EOF;
GET /1/statuses/sample.json HTTP/1.1\r
Authorization: Basic $auth\r
Host: stream.twitter.com\r
User-Agent: net-twitter-stream/0.1\r
Content-Type: application/x-www-form-urlencoded\r
Content-Length: $cl\r
\r
EOF
my $sock = IO::Socket::INET->new ( PeerAddr => 'stream.twitter.com:https' );
#$sock->print ( "$req$content" );
while ( my $l = $sock->getline ) {
last if $l =~ /^\s*$/;
}
while ( my $l = $sock->getline ) {
next if $l =~ /^\s*$/; # skip empty lines
$l =~ s/[^a-fA-F0-9]//g; # stop hex from compaining about \r
my $jsonlen = hex ( $l );
last if $jsonlen == 0;
eval {
my $json;
my $len = $sock->read ( $json, $jsonlen );
my $o = from_json ( $json );
$self->{got_tweet} ( $o, $json );
};
}
$self->{connection_closed} ( $sock ) if $self->{connection_closed};
}
You don't need to post the source, we can pretty much figure it out. You should try one of the examples, but my advice is to use AnyEvent::Twitter::Stream which comes with a good example that you only have to modify a bit to get it running
sub parse_from_twitter_stream {
my $user = 'XXX';
my $password = 'YYYY';
my $stream = Net::Twitter::Stream->new ( user => $user, pass => $password,
callback => \&got_tweet,
connection_closed_cb => \&connection_closed,
track => SEARCH_TERM);
sub connection_closed {
sleep 1;
warn "Connection to Twitter closed";
parse_from_twitter_stream();#This isn't working for me -- can't get connection to reopen after disconnect
}
sub got_tweet {
my ( $tweet, $json ) = #_; # a hash containing the tweet
#Do stuff here
}
}