Skip to content

Instantly share code, notes, and snippets.

@semifor
Created May 10, 2013 00:02
Show Gist options
  • Select an option

  • Save semifor/5551531 to your computer and use it in GitHub Desktop.

Select an option

Save semifor/5551531 to your computer and use it in GitHub Desktop.
0
#!/usr/bin/perl
use strict;
use warnings FATAL => qw( all );
use Net::Twitter::Lite::WithAPIv1_1;
use Lingua::EN::Inflect qw(NO NUMWORDS);
use HTML::Entities qw(encode_entities_numeric);
use Try::Tiny;
use lib '../files/lib';
use Base::Data qw(data_file data_directory get_hash);
use Base::Convert qw(textify hashtagify);
use Base::Nifty qw(line);
use Twitter qw(twitter_accounts nt_with_retry);
use Twitter::Data;
sub transform_user_data {
my $string = shift;
if ($string) {
$string = encode_entities_numeric($string,'^\n\x20-\x25\x27-\x7e');
$string =~ s/[\r\n]+//g;
$string =~ s/\|/|/g;
}
else {
$string = '';
}
return $string;
}
my @accounts = twitter_accounts();
die "First account is not Lady Aleena" if $accounts[0] ne 'Lady_Aleena';
my @accounts_totals;
my %is_account;
$is_account{$_}++ for @accounts;
my %statuses = (
get_lists => 'owner',
list_memberships => 'member',
list_subscriptions => 'subscriber'
);
my $nt = Net::Twitter::Lite::WithAPIv1_1->new(
consumer_key => Twitter::consumer_key,
consumer_secret => Twitter::consumer_key_secret,
legacy_lists_api => 1,
);
my %LA_people = get_hash(
file => data_file("Twitter/users/Lady_Aleena","people.txt"),
headings => [qw(id screen_name name time_zone location last_status friends followers greet)],
);
for my $account (@accounts) {
line(0,$account);
$nt->{access_token} = Twitter::access_key($account);
$nt->{access_token_secret} = Twitter::access_key_secret($account);
my $short_account = $account;
$short_account =~ s/LadyAleena_//g;
my $directory = "Twitter/users/$account";
my $full_path = data_directory($directory);
mkdir($full_path) or die unless (-d $full_path);
my %files;
for (qw(people lists mentions retweets)) {
$files{$_} = data_file($directory,"$_.txt");
}
# Start account totals
my $account_totals = nt_with_retry($nt,'account_totals');
push @accounts_totals, "$account|".join("|",map($account_totals->{$_},qw(followers friends updates)));
for my $heading (qw(updates friends followers)) {
my $value = $account_totals->{$heading};
line(1,"$account has $value $heading");
}
# End account totals
# Start followers and friends
my %people;
if (-s $files{people}) {
%people = get_hash(
file => $files{people},
headings => [qw(id screen_name name time_zone location last_status friends followers greet)],
);
}
for my $people_type (qw(friends followers)) {
my $method_ids = "$people_type\_ids";
my %ids;
for ( my $cursor = -1; $cursor; ) {
my $people_list = nt_with_retry($nt,$method_ids => $account);
for (@{$people_list->{ids}}) {
$people{$_}{greet} = 0 if !exists($people{$_});
$people{$_}{id} = $_;
$people{$_}{$people_type} = 1;
}
for (@{$people_list->{ids}}) {
$ids{$_}++;
}
$cursor = $people_list->{next_cursor};
}
for my $person (keys %people) {
if (!exists($ids{$person}) && $people{$person}{$people_type} && $people{$person}{$people_type} == 1) {
$people{$person}{$people_type} = -1;
line(2,"$people{$person}{screen_name} is now part of de$people_type.");
}
}
}
my @people_ids = keys %people;
my @lookups;
while (@people_ids) {
push @lookups, [splice(@people_ids,0,100)];
}
for (@lookups) {
my $user_list = nt_with_retry($nt,lookup_users => { user_id => [@{$_}], include_entities => 'true' });
for my $user (@{$user_list}) {
my $id = $user->{id};
$people{$id}{id} = $id;
$people{$id}{screen_name} = $user->{screen_name};
$people{$id}{name} = transform_user_data($user->{name});
$people{$id}{time_zone} = $user->{time_zone} ? $user->{time_zone} : '';
$people{$id}{location} = transform_user_data($user->{location});
$people{$id}{last_status} = $user->{status}{created_at};
}
}
my %groups;
my @greetings;
for my $person (keys %people) {
for (qw(friends followers)) {
$people{$person}{$_} = 0 if !defined($people{$person}{$_});
}
if ($people{$person}{followers} == 1 && $people{$person}{friends} != 1 && $people{$person}{greet} == 0) {
push @greetings, $people{$person}{screen_name};
}
$groups{companions}++ if $people{$person}{friends} == 1 && $people{$person}{followers} == 1;
$groups{unfriends}++ if $people{$person}{friends} == 0 && $people{$person}{followers} != -1;
$groups{unfollowers}++ if $people{$person}{followers} == 0 && $people{$person}{friends} != -1;
$groups{defriends}++ if $people{$person}{friends} == -1;
$groups{defollowers}++ if $people{$person}{followers} == -1;
}
for my $group (qw(companions unfriends unfollowers defriends defollowers)) {
if ($groups{$group}) {
my $singular = $group;
$singular =~ s/s$//;
my $count = NO($singular,$groups{$group});
line(1,"$account has $count.");
}
}
if ($account ne 'Lady_Aleena' && @greetings > 0) {
line(1,"$account\'s follower greetings");
for my $greetee (@greetings) {
if ($greetee) {
my $greeting = "\@$greetee Thank you for following \@Lady_Aleena\'s mostly automated $short_account account. I hope you don\'t get too bored here.";
line(2,$greeting);
nt_with_retry($nt,update => { status => $greeting });
for (keys %people) {
if ($people{$_}{screen_name} eq $greetee) {
$people{$_}{greet} = 1;
}
}
}
}
}
my @lines;
for my $person (sort {$a <=> $b} keys %people) {
my $line = join '|', map $people{$person}{$_} // 0, qw(id screen_name name time_zone location last_status friends followers greet);
push @lines, $line;
}
my $info = join("\n",@lines);
open(my $to_file,'>',data_file($directory,'people.txt')) or die "Can't open file $!";
print $to_file $info;
# End followers and friends
# Start list updates
# link to lists on tumblr http://tmblr.co/ZZVoswE7_RE1
line(1,"$account lists");
my %lists = Twitter::Data::lists_data($account);
my $lists_directory = "Twitter/users/$account/lists";
my $lists_full_path = data_directory($lists_directory);
mkdir($lists_full_path) or die "Can't make $lists_full_path $!" unless (-d $lists_full_path);
my @new_people;
for my $list_type (qw(get_lists list_memberships list_subscriptions)) {
my $list_heading = ucfirst textify($statuses{$list_type});
my $status = $statuses{$list_type};
line(2,$list_heading);
for ( my $cursor = -1; $cursor; ) {
my $lists_data = nt_with_retry($nt,$list_type => {
user => $account,
cursor => $cursor,
});
for my $list (sort {$a->{name} cmp $b->{name}} @{$lists_data->{lists}}) {
my $id = $list->{id};
$lists{$id}{id} = $id;
my $list_name = $list->{name};
push @{$lists{$id}{status}}, $status if !grep /$status/, @{$lists{$id}{status}};
line(3,$list_name);
next if $lists{$id}{done};
$lists{$id}{done} = 1;
my $list_slug = $lists{$id}{slug} = $list->{slug};
$lists{$id}{name} = $list->{name};
my $user_screen_name = $lists{$id}{user} = $list->{user}{screen_name};
my %changes = ();
my $member_count = $list->{member_count};
$changes{list_members} = $lists{$id}{members_change} = $member_count - ( $lists{$id}{members_count} // 0 );
my $subscriber_count = $list->{subscriber_count};
$changes{list_subscribers} = $lists{$id}{subscribers_change} = $subscriber_count - ( $lists{$id}{subscribers_count} // 0 );
$lists{$id}{members_count} = $member_count;
$lists{$id}{subscribers_count} = $subscriber_count;
next if $list_type ne "get_lists";
my $list_full_path = data_directory("Twitter/users/$account/lists/$list_slug");
mkdir($list_full_path) or die "Can't make $list_full_path $!" unless (-d $list_full_path);
for my $list_group (qw(list_members list_subscribers)) {
next if $changes{$list_group} == 0;
my @list_group_people;
for ( my $cursor = -1; $cursor; ) {
my $list_people = nt_with_retry($nt,$list_group => {
owner_screen_name => $user_screen_name,
slug => $list_slug,
cursor => $cursor,
});
push @list_group_people, map $_->{screen_name}, @{$list_people->{users}};
$cursor = $list_people->{next_cursor};
}
my $list_group_file_name = $list_group;
$list_group_file_name =~ s/^list_//;
open(my $list_group_fh, '>', "$list_full_path/$list_group_file_name.txt") or die "Can't open $list_full_path/$list_group_file_name.txt $!";
print $list_group_fh join("\n",@list_group_people);
close($list_group_fh);
next if ($account eq "Lady_Aleena" || $list_group ne "list_subscribers");
for my $person (@list_group_people) {
my $friends = 0;
for (values %people) {
$friends = $_->{friends} if $_->{screen_name} eq $person;
}
next if $friends != 0;
next if grep { $_->{screen_name} eq $person } values %people;
try {
my $new_user = nt_with_retry($nt,create_friend => {
screen_name => $person,
follow => 'true',
});
nt_with_retry($nt,update_friendship => {
screen_name => $person,
retweets => 'false',
});
my $id = $new_user->{id};
$people{$id}{id} = $id;
$people{$id}{screen_name} = $new_user->{screen_name};
$people{$id}{name} = transform_user_data($new_user->{name});
$people{$id}{time_zone} = $new_user->{time_zone};
$people{$id}{location} = transform_user_data($new_user->{location});
$people{$id}{last_status} = $new_user->{status}{created_at};
$people{$id}{friends} = 1;
line(4,"$person subscribed to $list_name so $short_account followed.");
my $line = join '|', map $people{$id}{$_} // 0, qw(id screen_name name time_zone location last_status friends followers greet);
push @new_people, $line;
}
catch {
line(4,"Could not follow $person.");
}
}
}
}
$cursor = $lists_data->{next_cursor};
}
}
if (scalar(@new_people) > 0) {
open(my $np_fh, '>>', $files{people}) or die "Can't open $files{people} $!";
print $np_fh join("\n",@new_people);
close($np_fh);
}
my @list_lines;
for my $list (values %lists) {
my $line = '';
for my $value (qw(id slug name user members_count members_change subscribers_count subscribers_change)) {
$line .= length $line ? "|".($list->{$value} // 0) : $list->{$value} // 0;
}
my $status = join(',',@{$list->{status}});
$line .= "|$status";
push @list_lines, $line;
}
if (scalar(@list_lines) > 0) {
open(my $al_fh, '>', $files{lists}) or die "Can't open $files{lists} $!";
print $al_fh join("\n",@list_lines);
close($al_fh);
}
# End list updates
# Start messages
my %last_messages;
for my $message (qw(mentions retweets)) {
my ($last_id,$last_data);
if (-s $files{$message}) {
open (my $message_fh, '<', $files{$message}) or die "Can't open $files{$message}. $!";
my @message_array = <$message_fh>;
chomp(@message_array);
my $last_line = pop @message_array;
($last_id,$last_data) = split(/\|/,$last_line,2);
}
$last_messages{$message} = $last_id ? $last_id : 1;
}
# End lists
# Start mentions
line(1,"$account mentions & replies");
my $mentions = nt_with_retry($nt,mentions => {
count => 200,
include_entities => 1,
since_id => $last_messages{mentions} || 1,
});
my @new_mentions;
for my $mention (@{$mentions}) {
my $tweet_id = $mention->{id};
my $tweet_dt = $mention->{created_at};
my $tweet_text = encode_entities_numeric($mention->{text},'^\n\x20-\x25\x27-\x7e');
$tweet_text =~ s/[\r\n]+//g;
my $tweet_reply_to_screen_name = $mention->{in_reply_to_screen_name} || '';
my $tweet_reply_to_id = $mention->{in_reply_to_status_id} || '';
my $user_id = $mention->{user}{id};
my $user_name = $mention->{user}{screen_name};
for my $url (@{$mention->{entities}{urls}}) {
my $short_url = quotemeta($url->{url});
my $long_url = $url->{expanded_url} ? $url->{expanded_url} : "http://$short_url";
$tweet_text =~ s/$short_url/$long_url/;
}
if (!exists($is_account{$user_name})) {
push @new_mentions, "$tweet_id|$user_name|$tweet_dt|$tweet_text|$tweet_reply_to_screen_name|$tweet_reply_to_id";
}
try {
if ($tweet_text =~ /^#FF/i || $tweet_text =~ /^#FollowFriday/i) {
$nt->update({
status => "\@$user_name Thank you for the \#FF.",
in_reply_to_status_id => $tweet_id,
});
}
}
catch {
print STDERR $_;
}
}
if (scalar(@new_mentions) > 0) {
local $\ = undef;
open (my $message_fh, '>>', $files{mentions}) or die "Can't open $files{mentions}. $!";
print join("\n",reverse @new_mentions);
print $message_fh "\n".join("\n",reverse @new_mentions);
close($message_fh);
print "\n";
}
# End mentions
# Start retweets
line(1,"$account reweets");
my %stored_retweets;
if (-f $files{retweets}) {
%stored_retweets = get_hash(
file => $files{retweets},
headings => [qw(id tweet retweeters)],
);
my @retweets_ids = (sort {$a <=> $b} keys %stored_retweets);
my @last_five_retweets = scalar(@retweets_ids) > 5 ? @retweets_ids[-5..-1] : @retweets_ids;
for my $retweet_id (@last_five_retweets) {
my $retweet_by = nt_with_retry($nt, retweets => $retweet_id);
my @retweeters_list = split(/,/,$stored_retweets{$retweet_id}{retweeters});
for my $retweeter (@{$retweet_by}) {
my $screen_name = $retweeter->{user}{screen_name};
push @retweeters_list, $screen_name if !grep($screen_name,@retweeters_list);
}
$stored_retweets{$retweet_id}{retweeters} = scalar(@retweeters_list) > 0 ? join(',',@retweeters_list) : '';
}
}
my $retweets = nt_with_retry($nt,retweets_of_me => {
count => 100,
include_entities => 1,
since_id => $last_messages{retweets} || 1,
});
for my $retweet (@{$retweets}) {
my $id = $retweet->{id};
my $retweet_by = nt_with_retry($nt, retweets => $id);
if (!exists($stored_retweets{$id})) {
my $retweet_text = encode_entities_numeric($retweet->{text},'^\n\x20-\x25\x27-\x7e');
for my $url (@{$retweet->{entities}{urls}}) {
my $short_url = quotemeta($url->{url});
my $long_url = $url->{expanded_url} ? $url->{expanded_url} : "http://$short_url";
$retweet_text =~ s/$short_url/$long_url/;
}
$stored_retweets{$id}{tweet} = $retweet_text;
my @retweeters_list;
for my $retweeter (@{$retweet_by}) {
my $screen_name = $retweeter->{user}{screen_name};
push @retweeters_list, $screen_name;
}
$stored_retweets{$id}{retweeters} = scalar(@retweeters_list) > 0 ? join(',',@retweeters_list) : '';
}
$stored_retweets{$id}{id} = $id;
}
my @updated_retweets;
for my $tweet (sort {$stored_retweets{$a}{id} <=> $stored_retweets{$b}{id}} keys %stored_retweets) {
my $id = $stored_retweets{$tweet}{id};
my $retweet_text = $stored_retweets{$tweet}{tweet};
my $retweeters = $stored_retweets{$tweet}{retweeters};
push @updated_retweets, "$id|$retweet_text|$retweeters";
}
if (scalar(@updated_retweets) > 0) {
local $\ = undef;
open (my $message_fh, '>', $files{retweets}) or die "Can't open $files{retweets}. $!";
print join("\n",@updated_retweets);
print $message_fh join("\n",@updated_retweets);
close($message_fh);
print "\n";
}
# End retweets
# End messages
}
open(my $gen_acct_file,'>',data_file('Twitter','account_totals.txt')) or die "Can't open account_totals.txt. $!";
print $gen_acct_file join("\n",@accounts_totals);
close($gen_acct_file);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment