forked from RSS-Bridge/rss-bridge
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Vk2Bridge] Alternative bridge for VK
- Loading branch information
Showing
1 changed file
with
251 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
<?php | ||
|
||
class Vk2Bridge extends BridgeAbstract | ||
{ | ||
const MAINTAINER = 'em92'; | ||
const NAME = 'VK'; | ||
const URI = 'https://vk.com'; | ||
const CACHE_TIMEOUT = 300; // 5min | ||
const PARAMETERS = [ | ||
[ | ||
'u' => [ | ||
'name' => 'Адрес группы или профиля', | ||
'exampleValue' => 'goblin_oper_ru', | ||
'required' => true | ||
], | ||
'hide_reposts' => [ | ||
'name' => 'Скрыть репосты', | ||
'type' => 'checkbox', | ||
] | ||
] | ||
]; | ||
|
||
const CONFIGURATION = [ | ||
'access_token' => [ | ||
'required' => true, | ||
], | ||
]; | ||
|
||
protected $videos = []; | ||
protected $ownerNames = []; | ||
protected $pageName; | ||
|
||
public function getURI() | ||
{ | ||
if (!is_null($this->getInput('u'))) { | ||
return urljoin(static::URI, urlencode($this->getInput('u'))); | ||
} | ||
|
||
return parent::getURI(); | ||
} | ||
|
||
public function getName() | ||
{ | ||
if ($this->pageName) { | ||
return $this->pageName; | ||
} | ||
|
||
return parent::getName(); | ||
} | ||
|
||
protected function getPostURI($post) | ||
{ | ||
$r = 'https://vk.com/wall' . $post['owner_id'] . '_'; | ||
if (isset($post['reply_post_id'])) { | ||
$r .= $post['reply_post_id'] . '?reply=' . $post['id'] . '&thread=' . $post['parents_stack'][0]; | ||
} else { | ||
$r .= $post['id']; | ||
} | ||
return $r; | ||
} | ||
|
||
// This function is based on SlackCoyote's vkfeed2rss | ||
// https://github.com/em92/vkfeed2rss | ||
protected function generateContentFromPost($post) | ||
{ | ||
// it's what we will return | ||
$ret = $post['text']; | ||
|
||
// html special characters convertion | ||
$ret = htmlentities($ret, ENT_QUOTES | ENT_HTML401); | ||
// change all linebreak to HTML compatible <br /> | ||
$ret = nl2br($ret); | ||
|
||
$ret = "<p>$ret</p>"; | ||
|
||
// find URLs | ||
$ret = preg_replace( | ||
'/((https?|ftp|gopher)\:\/\/[a-zA-Z0-9\-\.]+(:[a-zA-Z0-9]*)?\/?([@\w\-\+\.\?\,\'\/&%\$#\=~\x5C])*)/', | ||
"<a href='$1'>$1</a>", | ||
$ret | ||
); | ||
|
||
// find [id1|Pawel Durow] form links | ||
$ret = preg_replace('/\[(\w+)\|([^\]]+)\]/', "<a href='https://vk.com/$1'>$2</a>", $ret); | ||
|
||
|
||
// attachments | ||
if (isset($post['attachments'])) { | ||
// level 1 | ||
foreach ($post['attachments'] as $attachment) { | ||
if ($attachment['type'] == 'video') { | ||
// VK videos | ||
$title = e($attachment['video']['title']); | ||
$photo = e($this->getImageURLWithLargestWidth($attachment['video']['image'])); | ||
$href = "https://vk.com/video{$attachment['video']['owner_id']}_{$attachment['video']['id']}"; | ||
$ret .= "<p><a href='{$href}'><img src='{$photo}' alt='Video: {$title}'><br/>Video: {$title}</a></p>"; | ||
} elseif ($attachment['type'] == 'audio') { | ||
// VK audio | ||
$artist = e($attachment['audio']['artist']); | ||
$title = e($attachment['audio']['title']); | ||
$ret .= "<p>Audio: {$artist} - {$title}</p>"; | ||
} elseif ($attachment['type'] == 'doc' and $attachment['doc']['ext'] != 'gif') { | ||
// any doc apart of gif | ||
$doc_url = e($attachment['doc']['url']); | ||
$title = e($attachment['doc']['title']); | ||
$ret .= "<p><a href='{$doc_url}'>Документ: {$title}</a></p>"; | ||
} | ||
} | ||
// level 2 | ||
foreach ($post['attachments'] as $attachment) { | ||
if ($attachment['type'] == 'photo') { | ||
// JPEG, PNG photos | ||
// GIF in vk is a document, so, not handled as photo | ||
$photo = e($this->getImageURLWithLargestWidth($attachment['photo']['sizes'])); | ||
$text = e($attachment['photo']['text']); | ||
$ret .= "<p><img src='{$photo}' alt='{$text}'></p>"; | ||
} elseif ($attachment['type'] == 'doc' and $attachment['doc']['ext'] == 'gif') { | ||
// GIF docs | ||
$url = e($attachment['doc']['url']); | ||
$ret .= "<p><img src='{$url}'></p>"; | ||
} elseif ($attachment['type'] == 'link') { | ||
// links | ||
$url = e($attachment['link']['url']); | ||
$url = str_replace('https://m.vk.com', 'https://vk.com', $url); | ||
$title = e($attachment['link']['title']); | ||
if (isset($attachment['link']['photo'])) { | ||
$photo = $this->getImageURLWithLargestWidth($attachment['link']['photo']['sizes']); | ||
$ret .= "<p><a href='{$url}'><img src='{$photo}' alt='{$title}'><br>{$title}</a></p>"; | ||
} else { | ||
$ret .= "<p><a href='{$url}'>{$title}</a></p>"; | ||
} | ||
} elseif ($attachment['type'] == 'note') { | ||
// notes | ||
$title = e($attachment['note']['title']); | ||
$url = e($attachment['note']['view_url']); | ||
$ret .= "<p><a href='{$url}'>{$title}</a></p>"; | ||
} elseif ($attachment['type'] == 'poll') { | ||
// polls | ||
$question = e($attachment['poll']['question']); | ||
$vote_count = $attachment['poll']['votes']; | ||
$answers = $attachment['poll']['answers']; | ||
$ret .= "<p>Poll: {$question} ({$vote_count} votes)<br />"; | ||
foreach ($answers as $answer) { | ||
$text = e($answer['text']); | ||
$votes = $answer['votes']; | ||
$rate = $answer['rate']; | ||
$ret .= "* {$text}: {$votes} ({$rate}%)<br />"; | ||
} | ||
$ret .= '</p>'; | ||
} elseif (!in_array($attachment['type'], ['video', 'audio', 'doc'])) { | ||
$ret .= "<p>Unknown attachment type: {$attachment['type']}</p>"; | ||
} | ||
} | ||
} | ||
|
||
return $ret; | ||
} | ||
|
||
protected function getImageURLWithLargestWidth($items) { | ||
usort($items, function($a, $b) { | ||
return $b['width'] - $a['width']; | ||
}); | ||
return $items[0]['url']; | ||
} | ||
|
||
public function collectData() | ||
{ | ||
$r = $this->api('wall.get', [ | ||
'domain' => $this->getInput('u'), | ||
'extended' => '1', | ||
]); | ||
|
||
// TODO: ownernames to local variable | ||
|
||
// preparing ownerNames dictionary | ||
foreach ($r['response']['profiles'] as $profile) { | ||
$this->ownerNames[$profile['id']] = $profile['first_name'] . ' ' . $profile['last_name']; | ||
} | ||
foreach ($r['response']['groups'] as $group) { | ||
$this->ownerNames[-$group['id']] = $group['name']; | ||
} | ||
$this->generateFeed($r); | ||
} | ||
|
||
protected function generateFeed($r) { | ||
$ownerId = 0; | ||
|
||
foreach ($r['response']['items'] as $post) { | ||
if (!$ownerId) { | ||
$ownerId = $post['owner_id']; | ||
} | ||
$item = new FeedItem(); | ||
$content = $this->generateContentFromPost($post); | ||
if (isset($post['copy_history'])) { | ||
if ($this->getInput('hide_reposts')) { | ||
continue; | ||
} | ||
$originalPost = $post['copy_history'][0]; | ||
if ($originalPost['from_id'] < 0) { | ||
$originalPostAuthorScreenName = 'club' . (-$originalPost['owner_id']); | ||
} else { | ||
$originalPostAuthorScreenName = 'id' . $originalPost['owner_id']; | ||
} | ||
$originalPostAuthorURI = 'https://vk.com/' . $originalPostAuthorScreenName; | ||
$originalPostAuthorName = $this->ownerNames[$originalPost['from_id']]; | ||
$originalPostAuthor = "<a href='$originalPostAuthorURI'>$originalPostAuthorName</a>"; | ||
$content .= '<p>Репост (<a href="'; | ||
$content .= $this->getPostURI($originalPost); | ||
$content .= '">Пост</a> от '; | ||
$content .= $originalPostAuthor; | ||
$content .= '):</p>'; | ||
$content .= $this->generateContentFromPost($originalPost); | ||
} | ||
$item->setContent($content); | ||
$item->setTimestamp($post['date']); | ||
$item->setAuthor($this->ownerNames[$post['from_id']]); | ||
$item->setTitle($this->getTitle(strip_tags($content))); | ||
$item->setURI($this->getPostURI($post)); | ||
|
||
$this->items[] = $item; | ||
} | ||
|
||
$this->pageName = $this->ownerNames[$ownerId]; | ||
} | ||
|
||
protected function getTitle($content) | ||
{ | ||
$content = explode('<br>', $content)[0]; | ||
$content = strip_tags($content); | ||
preg_match('/^[:\,"\w\ \p{L}\(\)\?#«»\-\–\—||&\.%\\₽\/+\;\!]+/mu', htmlspecialchars_decode($content), $result); | ||
if (count($result) == 0) { | ||
return 'untitled'; | ||
} | ||
return $result[0]; | ||
} | ||
|
||
protected function api($method, array $params) | ||
{ | ||
$access_token = $this->getOption('access_token'); | ||
if (!$access_token) { | ||
returnServerError('You cannot run VK API methods without access_token'); | ||
} | ||
$params['v'] = '5.131'; | ||
$params['access_token'] = $access_token; | ||
$r = json_decode(getContents('https://api.vk.com/method/' . $method . '?' . http_build_query($params)), true); | ||
if (isset($r['error'])) { | ||
returnServerError('API returned error: ' . $r['error']['error_msg'] . ' (' . $r['error']['error_code'] . ')'); | ||
} | ||
return $r; | ||
} | ||
} |