#!/usr/bin/perl
=head1 NAME
lj-community-search.pl
=head1 AUTHOR
dimio (http://dimio.org)
=head1 DESCRIPTION
A simple tool to search for livejournal communities.
It allows you to find the posts of this author in these communities.
=head1 SYNOPSYS
See usage() subroutine for usage summary.
=head1 SEE ALSO
http://dimio.org/?p=1448
http://dimio.org/lj-tools
https://github.com/dimio/libperl-lj-light
=cut
BEGIN {
unshift @INC, '/home/dimiomnk/perl/usr/lib/perl5/libperl-lj-light/lib';
}
require 5.008_008;
use warnings;
use strict;
use utf8;
use FindBin qw ($Bin $Script);
use LWP::UserAgent;
use LJ::Light;
our $VERSION = '0.19'; # 2016-03-10
# set 'print' like a 'say'
$\ = $/;
usage() if !@ARGV;
my $options = {
postername => shift,
show_posts_cnt => shift, # 50 max
communities_cnt => shift, # max or -1 for unlim
communities => shift,
};
my $re_ljusername = qr/^[a-z0-9][-\w]{1,15}[a-z0-9]$/i;
$options->{postername} =~ $re_ljusername or die "Bad username: \"$options->{postername}\"";
$options->{show_posts_cnt} =~ m#^\d{1,2}$#
or warn "Bad posts count: \"$options->{show_posts_cnt}\"";
$options->{show_posts_cnt} = 50 if $options->{show_posts_cnt} > 50;
$options->{communities} =~ m#^[-,\w\s]{1,5000}$# or die 'Bad community name or communities list is too long!';
my @communities = split( /[,\s]{1,2}/, delete $options->{communities} );
if ( scalar @communities > $options->{communities_cnt} && $options->{communities_cnt} != '-1' ) {
splice( @communities, $options->{communities_cnt} );
}
my $prefs = {
flat_url => 'http://www.livejournal.com/interface/flat',
base_url => 'http://www.livejournal.com/',
userinfo_url => 'http://www.livejournal.com/userinfo.bml?user=',
conf_file => "$Bin/lj-community-search.conf",
login => '',
hpassword => '', # md5 hash
};
( $prefs->{login}, $prefs->{hpassword} ) = read_config( $prefs->{conf_file} );
if ( !$prefs->{login} or !$prefs->{hpassword} ) {
print 'Login or password for LJ account not found. See usage:', $/;
usage();
}
my $ua = LWP::UserAgent->new(
agent => "LJ Search Client v. $VERSION",
timeout => 360,
);
my $lj_client = LJ::Light->new(
login => $prefs->{login},
hpass => $prefs->{hpassword},
ua => $ua,
);
my $user_info = $lj_client->get_userinfo( $options->{postername}, [ 'id', ] );
my $results
= result_make_raw( \@communities, $lj_client, $options->{show_posts_cnt}, $user_info->{id}, );
my $results_formatted = result_make_formatted($results);
results_print_formatted($results_formatted);
exit;
=head1 SUBROUTINES
=over
=item usage()
Printing usage for this script.
=cut
sub usage {
print <
All options are required!
postername - username of LJ user for which to search posts
show_posts_cnt - count of posts per community to get (50 max)
communities_cnt - max count of processed communities (-1 for unlim)
communities - list of communities for search posts (separated by commas)
EXAMPLE:
perl $Script "dimio-blog" "3" "-1" "ru-auto,ru-perl,ru-kino"
EOF
exit;
}
=item read_config( $conf_file_path )
Read the config file with login data.
File should contain two lines:
login
password (or hpassword - md5 hexdigest from password)
=cut
sub read_config {
my $conf = shift;
my ( $login, $pass );
open( my $fh, '<', $conf )
or die "Can't open config '$conf': $!";
my @login_pass = <$fh>;
close($fh)
or warn "Can't close config '$conf': $!";
chomp @login_pass;
return @login_pass;
}
=item results_print_formatted( $results_formatted )
Print the results in format:
COMMUNITY_NAME_N1
No posts in community_name_N1
COMMUNITY_NAME_N2
dates of posts
links to posts found
etc...
=cut
sub results_print_formatted {
my $results = shift;
foreach my $community ( keys %$results ) {
print uc($community);
if ( !keys %{ $results->{$community} } ) {
print 'No posts in ', $community;
{ local $\; print $/; }
next;
}
foreach my $date ( sort { $b cmp $a } keys %{ $results->{$community} } ) {
my @year_month_day = split( '-', $date );
print join( '-', reverse @year_month_day );
# print url's for specified date
print join( $/, @{ $results->{$community}->{$date} } );
}
{ local $\; print $/; }
}
}
=item result_make_formatted( $results_raw )
Get results raw, convert it to format:
{
community_name_N =>
{
date =>
[
post_url_1, .. , post_url_N,
]
}
}
=cut
sub result_make_formatted {
my $results_raw = shift;
my $results_formatted = {};
foreach my $community ( keys %$results_raw ) {
# save empty hash with community name for 'no posts' server response
$results_formatted->{$community} = {} if !$results_formatted->{$community};
foreach my $event_num ( keys %{ $results_raw->{$community} } ) {
foreach ( keys %{ $results_raw->{$community}->{$event_num} } ) {
my $date = delete $results_raw->{$community}->{$event_num}
->{ 'events_' . $event_num . '_eventtime' };
next if !$date;
# del posting time from date
$date =~ s/\s[\w:]+$//;
my $url = delete $results_raw->{$community}->{$event_num}
->{ 'events_' . $event_num . '_url' };
$results_formatted->{$community}->{$date} = []
if !$results_formatted->{$community}->{$date};
push( @{ $results_formatted->{$community}->{$date} }, $url );
# experimental push in array ref
#push( $results_formatted->{$date}, $url );
}
}
}
return $results_formatted;
}
=item result_make_raw(
\@communities, $lj_client, $lj_events, $show_posts_cnt, $poster_id,
)
Make raw results in format:
{
community_name_N =>
{
event_number =>
{
post_date => date time,
post_url => url,
}
}
}
=back
=cut
sub result_make_raw {
my $communities = shift;
my $lj_client = shift;
my $show_posts_cnt = shift;
my $poster_id = shift;
my $results = {};
foreach my $community (@$communities) {
next if exists $results->{$community};
$community =~ s/\s//g;
$community =~ $re_ljusername or die "Bad community name: \"$community\"";
# save empty hash with community name for 'no posts' server response
#$results->{$community} = {} if !$results->{$community};
$results->{$community} = {};
my $entries = $lj_client->events->get_entries_by_poster(
show_posts_cnt => $show_posts_cnt,
target_community => $community,
poster => $poster_id,
auth => $lj_client->auth->challenge,
);
foreach my $entry ( keys %$entries ) {
if ( $entry =~ m{^(events_(\d+)_eventtime)$} ) {
$results->{$community}->{$2}->{$1} = $entries->{$entry};
}
if ( $entry =~ m{^(events_(\d+)_url)$} ) {
$results->{$community}->{$2}->{$1} = $entries->{$entry};
}
}
#undef $entries;
}
return $results;
}