Net-Mollom

 view release on metacpan or  search on metacpan

lib/Net/Mollom.pm  view on Meta::CPAN


=head1 SYNOPSIS

Communicate with the Mollom web API (L<http://mollom.com/>) via
XML-RPC to determine whether user input is Spam, Ham, flame or
obscene.

    my $mollom = Net::Mollom->new(
        public_key  => 'a2476604ffba00c907478c8f40b83b03',
        private_key => '42d5448f124966e27db079c8fa92de0f',
    );

    my @server_list = $mollom->server_list();

    my $check = $mollom->check_content(
        post_title => $title,
        post_body  => $text,
    );
    if ($check->is_spam) {
        warn "someone's trying to sell us v1@grA!";
    } elsif ($check->is_unsure) {

        # show them a CAPTCHA to see if they are really human
        my $captcha_url = $mollom->get_image_captcha();
    } elsif ($check->quality < .5) {
        warn "someone's trying to flame us!";
    }

If you have any questions about how any of the methods work, please
consult the Mollom API documentation - L<http://mollom.com/api>.

=head1 CONSTRUCTORS

=head2 new

This creates a new NET::Mollom object for communication. It takes the following
named arguments:

=over

=item * public_key (required)

This is your Mollom API public key.

=item * private_key (required)

This is your Mollom API private key.

=item * attempt_limit

This is the number of times Net::Mollom will try to refresh the server list
before giving up. Defaults to 1.

=item * warnings

This boolean turns on warnings. You will get warnings for the following
situations:

=over

=item * A Mollom server is busy and we need to try a different one.

=item * We have exhausted the list of servers to try and we need to get a new list.

=back

=back

=head1 OBJECT METHODS

=head2 verify_key

Check to make sure that Mollom recognizes your public and private keys.
Returns true if successful, false otherwise. This is not necessary to use
in your application, but can be used when doing initial development or testing.

    if( $mollom->verify_key ) {
        # go a head and do stuff
    } else {
        # doh! you screwed up somewhere
    }

=cut

sub verify_key {
    my $self = shift;
    # get the server list from Mollom if we don't already have one
    $self->server_list() unless $self->servers_init;
    return $self->_make_api_call('verifyKey');
}

=head2 check_content

Check some content for spamminess and quality. Takes the following
optional named arguments:

=over

=item * post_title

=item * post_body

=item * author_name

=item * author_url

=item * author_mail

=item * author_openid

=item * author_ip

=item * author_id

=back

Returns a L<Net::Mollom::ContentCheck> object.

    my $check = $mollom->check_content(
        post_title  => $title,
        post_body   => $body,

lib/Net/Mollom.pm  view on Meta::CPAN

                type => SCALAR,
                regex =>
                  qr/^(total_(days|accepted|rejected)|yesterday_(accepted_rejected)|today_(accepted_rejected))$/
            },
        }
    );

    # get the server list from Mollom if we don't already have one
    $self->server_list() unless $self->servers_init;
    return $self->_make_api_call('getStatistics', \%args);
}

sub _make_api_call {
    my ($self, $function, $args) = @_;
    my $secret = $self->private_key;
    my @servers = @{$self->servers};

    # keep track of how many times we've descended down into this rabbit hole
    if( !  $self->{_recurse_level} ) {
        $self->{_recurse_level} = 1;
    } else {
        $self->{_recurse_level}++;
    }

    if (!$self->xml_rpc) {
        my $xml_rpc = eval { XML::RPC->new($servers[$self->current_server] . '/' . $API_VERSION) };
        Net::Mollom::CommunicationException->throw(error => $@) if $@;
        $self->xml_rpc($xml_rpc);
    }

    $args->{public_key} ||= $self->public_key;
    $args->{time}       ||= DateTime->now->strftime('%Y-%m-%dT%H:%M:%S.000%z');
    $args->{nonce}      ||= int(rand(2_147_483_647));                          # rand 32 bit integer
    $args->{hash} ||=
      encode_base64(hmac_sha1(join(':', $args->{time}, $args->{nonce}, $secret), $secret));

    if (   $function ne 'getServerList'
        && $function ne 'verifyKey'
        && $function ne 'getStatistics'
        && $self->session_id)
    {
        $args->{session_id} = $self->session_id;
    }

    my $results = eval { $self->xml_rpc->call("mollom.$function", $args) };
    Net::Mollom::CommunicationException->throw(error => $@) if $@;

    # check if there are any errors and handle them accordingly
    if (ref $results && (ref $results eq 'HASH') && $results->{faultCode}) {
        my $fault_code = $results->{faultCode};
        if (($fault_code == $ERROR_REFRESH_SERVERS) && ($self->{_recurse_level} < $MAX_API_TRIES) ) {
            if ($function eq 'getServerList') {
                delete $self->{_recurse_level};
                Net::Mollom::ServerListException->throw(error => "Could not get list of servers from Mollom!");
            } else {
                $self->servers_init(0);
                $self->server_list;
                return $self->_make_api_call($function, $args);
            }
        } elsif (($fault_code == $ERROR_NEXT_SERVER) && ($self->{_recurse_level} < $MAX_API_TRIES)) {
            carp("Mollom server busy, trying the next one.") if $self->warnings;
            my $next_index = $self->current_server + 1;
            if ($servers[$next_index] ) {
                $self->current_server($next_index);
                return $self->_make_api_call($function, $args);
            } else {
                # try to refresh the servers if we can
                if ($self->attempt_limit > $self->attempts) {
                    sleep(1);
                    carp("No more servers to try. Attempting to refresh server list.")
                      if $self->warnings;
                    $self->attempts($self->attempts + 1);
                    $self->servers_init(0);
                    $self->server_list;
                    return $self->_make_api_call($function, $args);
                } else {
                    Net::Mollom::ServerListException->throw(error => "No more Mollom servers to try!");
                }
            }
        } elsif( $self->{_recurse_level} < $MAX_API_TRIES ) {
            delete $self->{_recurse_level};
            Net::Mollom::APIException->throw(
                error => "Error communicating with Mollom [$results->{faultCode}]: $results->{faultString}",
                mollom_code => $results->{faultCode},
                mollom_desc => $results->{faultString},
            );
        } else {
            my $msg = qq(Tried making API call "$function" $self->{_recurse_level} times but failed.)
              . " Giving up";
            delete $self->{_recurse_level};
            Net::Mollom::APIException->throw(
                error       => $msg,
                mollom_code => $results->{faultCode},
                mollom_desc => $results->{faultString},
            );
        }
    } else {
        $self->attempts(0);
        delete $self->{_recurse_level} unless $function eq 'getServerList';
        return $results;
    }
}

=head1 EXCEPTIONS

Any object method can throw a L<Net::Mollom::Exception> object (using L<Exception::Class> underneath).

The following exceptions are possible:

=head2 Net::Mollom::ServerListException

This happens when we've exhausted the list available servers and we've reached
our C<attempt_limit> for getting more.

=head2 Net::Mollom::APIException

There was some kind of problem communicating with the Mollom service.
This is not a network error, but somehow we're not talking to it in a language
it can understand (maybe an API change or bug in Net::Mollom, etc).

=head2 Net::Mollom::CommunicationException



( run in 1.448 second using v1.01-cache-2.11-cpan-39bf76dae61 )