| blib/lib/VK.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 1 | 3 | 33.3 |
| branch | n/a | ||
| condition | n/a | ||
| subroutine | 1 | 1 | 100.0 |
| pod | n/a | ||
| total | 2 | 4 | 50.0 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | #!/usr/bin/perl | ||||||
| 2 | |||||||
| 3 | ################################################### | ||||||
| 4 | ## ## | ||||||
| 5 | ## VKontakte serverside manager ## | ||||||
| 6 | ## ## | ||||||
| 7 | ## Marat Shaymardanov, LeonMedia LLC, 2013 ## | ||||||
| 8 | ## info@leonmedia.ru http://leonmedia.ru ## | ||||||
| 9 | ## ## | ||||||
| 10 | ## http://vk.com/do.more ## | ||||||
| 11 | ## ## | ||||||
| 12 | ################################################### | ||||||
| 13 | |||||||
| 14 | package VK; | ||||||
| 15 | |||||||
| 16 | require Exporter; | ||||||
| 17 | |||||||
| 18 | @ISA = qw(Exporter); | ||||||
| 19 | @EXPORT = qw(); | ||||||
| 20 | |||||||
| 21 | 1 | 1 | 23764 | use WWW::Mechanize::GZip; | |||
| 0 | |||||||
| 0 | |||||||
| 22 | use URI::Escape; | ||||||
| 23 | |||||||
| 24 | our $VERSION = '0.09'; | ||||||
| 25 | |||||||
| 26 | sub new | ||||||
| 27 | { | ||||||
| 28 | my ($class, $login, $pass, $wallurl, $security_code) = @_; | ||||||
| 29 | my $self = {}; | ||||||
| 30 | |||||||
| 31 | bless $self, $class; | ||||||
| 32 | |||||||
| 33 | my $mech = WWW::Mechanize::GZip->new( | ||||||
| 34 | agent => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13', | ||||||
| 35 | timeout => 30, | ||||||
| 36 | onerror => sub {print "error\n"} | ||||||
| 37 | ); | ||||||
| 38 | |||||||
| 39 | $mech->cookie_jar(HTTP::Cookies->new()); | ||||||
| 40 | $self->{mech} = $mech; | ||||||
| 41 | |||||||
| 42 | if ($login){ | ||||||
| 43 | if (!$self->login($login, $pass, $wallurl, $security_code)){ | ||||||
| 44 | return undef; | ||||||
| 45 | } | ||||||
| 46 | } | ||||||
| 47 | |||||||
| 48 | return $self; | ||||||
| 49 | } | ||||||
| 50 | |||||||
| 51 | sub login | ||||||
| 52 | { | ||||||
| 53 | my ($self, $login, $pass, $wallurl, $security_code) = @_; | ||||||
| 54 | my $mech = $self->{mech}; | ||||||
| 55 | |||||||
| 56 | $self->{security_code} = $security_code; | ||||||
| 57 | $self->get("http://vk.com/login"); | ||||||
| 58 | |||||||
| 59 | # correct language | ||||||
| 60 | $mech->content =~ m/hash: '([^']+)'/s; | ||||||
| 61 | $mech->post("http://vk.com/al_index.php",{ | ||||||
| 62 | 'act' => 'change_lang', | ||||||
| 63 | 'lang_id' => 0, # russian | ||||||
| 64 | 'hash' => $1 | ||||||
| 65 | }); | ||||||
| 66 | |||||||
| 67 | $mech->form_number(1); | ||||||
| 68 | $mech->field("email" => $login); | ||||||
| 69 | $mech->field("pass" => $pass); | ||||||
| 70 | |||||||
| 71 | my $r = $mech->submit(); | ||||||
| 72 | my $c = $r->content; | ||||||
| 73 | my $wallid = undef; | ||||||
| 74 | |||||||
| 75 | if ($c =~ m/parent\.onLoginDone\('([^']+)'\)/is){ | ||||||
| 76 | $self->{home} = $1; | ||||||
| 77 | |||||||
| 78 | # wall hash | ||||||
| 79 | if ($wallurl && ($wallurl ne $self->{home})){ | ||||||
| 80 | $self->get($wallurl); | ||||||
| 81 | $c = $mech->content; | ||||||
| 82 | |||||||
| 83 | # wall hash | ||||||
| 84 | $c =~ m/"post_hash":"([^"]+)"/s; | ||||||
| 85 | $self->{wall_hash} = $1; | ||||||
| 86 | |||||||
| 87 | # wall oid | ||||||
| 88 | $c =~ m/"wall_oid":([^,]+),/s; | ||||||
| 89 | $wallid = $1; | ||||||
| 90 | } | ||||||
| 91 | |||||||
| 92 | # user hash | ||||||
| 93 | $self->get($self->{home}); | ||||||
| 94 | $c = $mech->content; | ||||||
| 95 | |||||||
| 96 | # get post hash | ||||||
| 97 | $c =~ m/\"post_hash\":\"([^\"]+)"/s; | ||||||
| 98 | $self->{hash} = $1; | ||||||
| 99 | |||||||
| 100 | # get user id | ||||||
| 101 | $c =~ m/id: (\d+)/s; | ||||||
| 102 | $self->{id} = $1; | ||||||
| 103 | |||||||
| 104 | $self->{wallurl} = $wallurl || $self->{home}; | ||||||
| 105 | $self->{wallid} = $wallid || $self->{id}; | ||||||
| 106 | |||||||
| 107 | return 1; | ||||||
| 108 | } | ||||||
| 109 | #print $c; | ||||||
| 110 | return 0; | ||||||
| 111 | } | ||||||
| 112 | |||||||
| 113 | sub createAlbum | ||||||
| 114 | { | ||||||
| 115 | my ($self, $title, $desc, $permission, $commentable) = @_; | ||||||
| 116 | my $mech = $self->{mech}; | ||||||
| 117 | |||||||
| 118 | $self->get('http://m.vk.com/photos?act=select_album'); | ||||||
| 119 | |||||||
| 120 | # get hash | ||||||
| 121 | if ($mech->content =~ m/photos\?act=new_album&hash=([\d\w]+)/){ | ||||||
| 122 | my $hash = $1; | ||||||
| 123 | |||||||
| 124 | $mech->post( | ||||||
| 125 | 'http://m.vk.com/photos?act=new_album&hash='.$hash,{ | ||||||
| 126 | 'title' => $title, | ||||||
| 127 | 'desc' => $desc, | ||||||
| 128 | 'view' => int($permission), | ||||||
| 129 | 'comm' => int($commentable) | ||||||
| 130 | }); | ||||||
| 131 | |||||||
| 132 | return ($mech->content =~ m/Альбом успешно создан\.<\/div>/)?1:0; | ||||||
| 133 | } | ||||||
| 134 | |||||||
| 135 | return 0; | ||||||
| 136 | } | ||||||
| 137 | |||||||
| 138 | sub get | ||||||
| 139 | { | ||||||
| 140 | my $self = shift @_; | ||||||
| 141 | my $mech = $self->{mech}; | ||||||
| 142 | from_security: | ||||||
| 143 | my $r = $mech->get(@_); | ||||||
| 144 | |||||||
| 145 | if ($mech->content =~ m/"loc":"\?act=security_check/s){ | ||||||
| 146 | $mech->content =~ m/{act: 'security_check', code: [^,]+, to: '([^']+)', al_page: '(\d+)', hash: '([^']+)'}/s; | ||||||
| 147 | $mech->post("http://vk.com/login.php", { | ||||||
| 148 | 'act' => 'security_check', | ||||||
| 149 | 'code' => $self->{security_code}, | ||||||
| 150 | 'to' => $1, | ||||||
| 151 | 'hash' => $3, | ||||||
| 152 | 'al_page' => $2 | ||||||
| 153 | }); | ||||||
| 154 | #print $mech->content."\n\n\n"; | ||||||
| 155 | goto from_security; | ||||||
| 156 | } | ||||||
| 157 | |||||||
| 158 | return $r; | ||||||
| 159 | } | ||||||
| 160 | |||||||
| 161 | sub logout | ||||||
| 162 | { | ||||||
| 163 | my ($self) = @_; | ||||||
| 164 | my $mech = $self->{mech}; | ||||||
| 165 | $self->get("http://m.vk.com"); | ||||||
| 166 | my $link = $mech->find_link( text_regex => qr/Выход/i ); | ||||||
| 167 | sleep(1); | ||||||
| 168 | my $r = $self->get($link->url()); | ||||||
| 169 | return $r->is_success; | ||||||
| 170 | } | ||||||
| 171 | |||||||
| 172 | sub addPhoto | ||||||
| 173 | { | ||||||
| 174 | my ($self, $filePath, $albumName, $albumDesc, $permission, $commentable) = @_; | ||||||
| 175 | my $mech = $self->{mech}; | ||||||
| 176 | |||||||
| 177 | $albumName = '#shared' if (!$albumName); | ||||||
| 178 | |||||||
| 179 | do_again: | ||||||
| 180 | $self->get("http://m.vk.com"); | ||||||
| 181 | sleep(2); | ||||||
| 182 | |||||||
| 183 | print "my photos\n"; | ||||||
| 184 | my $link = $mech->find_link( text_regex => qr/Мои Фотографии/is ); | ||||||
| 185 | my $r = $self->get($link->url()); | ||||||
| 186 | sleep(2); | ||||||
| 187 | |||||||
| 188 | print "add new photos\n"; | ||||||
| 189 | $link = $mech->find_link( text_regex => qr/Добавить новые фотографии/is ); | ||||||
| 190 | |||||||
| 191 | sleep(2); | ||||||
| 192 | $r = $self->get($link->url()); | ||||||
| 193 | my $c = $r->content; | ||||||
| 194 | |||||||
| 195 | print "create album if required $albumName\n"; | ||||||
| 196 | # check if album exists | ||||||
| 197 | unless ($c =~ m/ $albumName<\/div>/s){
|
||||||
| 198 | print $self->createAlbum($albumName, $albumDesc, $permission, $commentable)?"ok\n":"failed\n"; | ||||||
| 199 | goto do_again; | ||||||
| 200 | } | ||||||
| 201 | |||||||
| 202 | print "upload\n"; | ||||||
| 203 | if ($c =~ m/]+|)>\s+ ]+>\s+ $albumName<\/div>/s){
|
||||||
| 204 | print "submitting file $1\n"; | ||||||
| 205 | |||||||
| 206 | $r = $self->get($1); | ||||||
| 207 | =no | ||||||
| 208 | if ($mech->content =~ m/ В этом альбоме уже находится более 500 фотографий/){
|
||||||
| 209 | if ($albumName =~ m/\-(\d+)$/){ | ||||||
| 210 | my $next = int($1) + 1; | ||||||
| 211 | $albumName =~ s/\-\d+$/($next)/; | ||||||
| 212 | } else { | ||||||
| 213 | $albumName .= "-2"; | ||||||
| 214 | } | ||||||
| 215 | print "rename album to $albumName\n"; | ||||||
| 216 | goto do_again; | ||||||
| 217 | } | ||||||
| 218 | =cut | ||||||
| 219 | $mech->form_number(1); | ||||||
| 220 | $mech->field("file1" => $filePath); | ||||||
| 221 | |||||||
| 222 | sleep(2); | ||||||
| 223 | $r = $mech->submit(); | ||||||
| 224 | |||||||
| 225 | print $r->is_success()." - submit result\n"; | ||||||
| 226 | |||||||
| 227 | $c = $r->content; | ||||||
| 228 | if ($c =~ m/Загрузка завершена\.<\/div>/is){ | ||||||
| 229 | print "Uploaded $1\n" and return $1 if ($c =~ m/\"\/photo(\d+_\d+)/is); | ||||||
| 230 | } | ||||||
| 231 | } | ||||||
| 232 | print "not uploaded\n"; | ||||||
| 233 | |||||||
| 234 | return undef; | ||||||
| 235 | } | ||||||
| 236 | |||||||
| 237 | sub wallPost | ||||||
| 238 | { | ||||||
| 239 | my ($self, %params) = @_; | ||||||
| 240 | my $mech = $self->{mech}; | ||||||
| 241 | my $photoid = undef; | ||||||
| 242 | |||||||
| 243 | if ($params{'photo'}){ | ||||||
| 244 | $photoid = $self->addPhoto( | ||||||
| 245 | $params{'photo'}, | ||||||
| 246 | $params{'album'}, $params{'album_desc'}, | ||||||
| 247 | $params{'album_view'}, $params{'album_comments'} | ||||||
| 248 | ); | ||||||
| 249 | |||||||
| 250 | return 0 if (!$params{'post_anyway'} && !$photoid); | ||||||
| 251 | } | ||||||
| 252 | |||||||
| 253 | my $to_id = $params{'to_id'} || $self->{wallid}; | ||||||
| 254 | |||||||
| 255 | my $r = $self->get('http://vk.com'.(($to_id>0)?"/id$to_id":$self->{wallurl})); | ||||||
| 256 | $mech->content =~ m/"post_hash":"([^"]+)"/s; | ||||||
| 257 | |||||||
| 258 | my $post_hash = $1; | ||||||
| 259 | |||||||
| 260 | my $h = { | ||||||
| 261 | 'act' => 'post', | ||||||
| 262 | 'al' => 1, | ||||||
| 263 | 'hash' => $post_hash, | ||||||
| 264 | 'message' => $params{'message'}, | ||||||
| 265 | 'note_title' => $params{'note_title'}, | ||||||
| 266 | 'official' => $params{'official'}, | ||||||
| 267 | 'status_export' => '', | ||||||
| 268 | 'signed' => $params{'signed'}?1:0, | ||||||
| 269 | 'to_id' => $to_id, | ||||||
| 270 | 'type' => ($to_id > 0)?'all':'own', | ||||||
| 271 | }; | ||||||
| 272 | |||||||
| 273 | my $n = 0; | ||||||
| 274 | |||||||
| 275 | if ($photoid){ | ||||||
| 276 | $n++; | ||||||
| 277 | $h->{"attach$n\_type"} = 'photo'; | ||||||
| 278 | $h->{"attach$n"} = $photoid; | ||||||
| 279 | } | ||||||
| 280 | |||||||
| 281 | if ($params{'link'}){ | ||||||
| 282 | $n++; | ||||||
| 283 | $h->{"attach$n\_type"} = 'share'; | ||||||
| 284 | $h->{"title"} = $params{'link_title'}, | ||||||
| 285 | $h->{"description"} = $params{'link_desc'}, | ||||||
| 286 | $h->{"url"} = $params{'link'}, | ||||||
| 287 | } | ||||||
| 288 | |||||||
| 289 | #foreach $key (sort(keys %{$h})){ print "$key=$h->{$key}&"; } | ||||||
| 290 | sleep(2); | ||||||
| 291 | print "posting message\n"; | ||||||
| 292 | my $r = $mech->post("http://vk.com/al_wall.php", $h); | ||||||
| 293 | my @codes = split(/<\!>/, $r->content); | ||||||
| 294 | |||||||
| 295 | return ($codes[4] eq '0')?1:0; | ||||||
| 296 | } | ||||||
| 297 | |||||||
| 298 | 1; | ||||||
| 299 | __END__ |