Skip to content

Commit

Permalink
[Vk2Bridge] Alternative bridge for VK
Browse files Browse the repository at this point in the history
  • Loading branch information
em92 committed Dec 23, 2023
1 parent 98dafb6 commit a3b8847
Showing 1 changed file with 251 additions and 0 deletions.
251 changes: 251 additions & 0 deletions bridges/Vk2Bridge.php
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\-\+\.\?\,\'\/&amp;%\$#\=~\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;
}
}

0 comments on commit a3b8847

Please sign in to comment.