Skip to content

Instantly share code, notes, and snippets.

@svolobuev
Created May 6, 2015 20:52
Show Gist options
  • Save svolobuev/52aefa56abf0b302175c to your computer and use it in GitHub Desktop.
Save svolobuev/52aefa56abf0b302175c to your computer and use it in GitHub Desktop.
Vk Authorize (sf2 bundle)
<?php
namespace PrestoHeads\VkBotBundle\Api;
use PrestoHeads\VkBotBundle\VkAuthorize\VkAuthorize;
abstract class ApiResource
{
/**
* @var VkApi
*/
private $api;
/**
* @var VkAuthorize
*/
protected $vkAuth;
/**
* @var array
*/
protected $authData;
protected $token = null;
public function __construct(VkApi $api, VkAuthorize $vkAuth, $authData)
{
$this->api = $api;
$this->vkAuth = $vkAuth;
$this->authData = $authData;
}
/**
* @return VkApi
*/
protected function getApi()
{
$this->api->setAccessToken($this->getToken());
return $this->api;
}
/**
* @return null|string
* @throws \Exception
*/
protected function getToken()
{
if (is_null($this->token)) {
$this->token = $this->vkAuth->getAccessToken($this->authData['login'],$this->authData['password'],'desktop');
}
return $this->token;
}
}
<?php
namespace PrestoHeads\VkBotBundle\Api\Group;
use PrestoHeads\VkBotBundle\Api\ApiResource;
class Board extends ApiResource
{
public function deleteTopic($group_id, $topic_id)
{
$response = $this->getApi()->api('board.deleteTopic',[
'group_id' => $group_id,
'topic_id' => $topic_id
],true);
return $response;
}
public function restoreTopic($group_id, $topic_id)
{
throw new \Exception("not implemented");
$response = $this->getApi()->api('board.restoreTopic',[
'group_id' => $group_id,
'topic_id' => $topic_id
],true);
return $response;
}
public function deleteComment($group_id, $topic_id, $comment_id)
{
$response = $this->getApi()->api('board.deleteComment',[
'group_id' => $group_id,
'topic_id' => $topic_id,
'comment_id' => $comment_id
],true);
return $response;
}
public function restoreComment($group_id, $topic_id, $comment_id)
{
$response = $this->getApi()->api('board.restoreComment',[
'group_id' => $group_id,
'topic_id' => $topic_id,
'comment_id' => $comment_id
],true);
return $response;
}
}
<?php
namespace PrestoHeads\VkBotBundle\Api\Group;
use PrestoHeads\VkBotBundle\Api\ApiResource;
class Photo extends ApiResource
{
/**
* Delete comment from post group photo
* @param $group_id
* @param $comment_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function deleteComment($group_id, $comment_id)
{
$response = $this->getApi()->api('photo.deleteComment',[
'owner_id' => "-{$group_id}",
'comment_id' => $comment_id
],true);
return $response;
}
/**
* Restore comment from post group photo
* @param $group_id
* @param $comment_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function restoreComment($group_id, $comment_id)
{
$response = $this->getApi()->api('photo.restoreComment',[
'owner_id' => "-{$group_id}",
'comment_id' => $comment_id
],true);
return $response;
}
}
<?php
namespace PrestoHeads\VkBotBundle\Api\Group;
use PrestoHeads\VkBotBundle\Api\ApiResource;
class Video extends ApiResource
{
/**
* Delete comment from post group video
* @param $group_id
* @param $comment_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function deleteComment($group_id, $comment_id)
{
$response = $this->getApi()->api('video.deleteComment',[
'owner_id' => "-{$group_id}",
'comment_id' => $comment_id
],true);
return $response;
}
/**
* Restore comment from post group video
* @param $group_id
* @param $comment_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function restoreComment($group_id, $comment_id)
{
$response = $this->getApi()->api('video.restoreComment',[
'owner_id' => "-{$group_id}",
'comment_id' => $comment_id
],true);
return $response;
}
}
<?php
namespace PrestoHeads\VkBotBundle\Api\Group;
use PrestoHeads\VkBotBundle\Api\ApiResource;
class Wall extends ApiResource
{
/**
* Delete post from group wall
* @param $group_id
* @param $post_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function delete($group_id, $post_id)
{
$response = $this->getApi()->api('wall.delete',[
'owner_id' => "-{$group_id}",
'post_id' => $post_id
],true);
return $response;
}
/**
* Restore post from group wall
* @param $group_id
* @param $post_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function restore($group_id, $post_id)
{
$response = $this->getApi()->api('wall.restore',[
'owner_id' => "-{$group_id}",
'post_id' => $post_id
],true);
return $response;
}
/**
* Delete comment from post group wall
* @param $group_id
* @param $comment_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function deleteComment($group_id, $comment_id)
{
$response = $this->getApi()->api('wall.deleteComment',[
'owner_id' => "-{$group_id}",
'comment_id' => $comment_id
],true);
return $response;
}
/**
* Restore comment from post group wall
* @param $group_id
* @param $comment_id
* @return mixed
* @throws \Novanova\VK\VKException
*/
public function restoreComment($group_id, $comment_id)
{
$response = $this->getApi()->api('wall.restoreComment',[
'owner_id' => "-{$group_id}",
'comment_id' => $comment_id
],true);
return $response;
}
}
<?php
namespace PrestoHeads\VkBotBundle\Api;
use Novanova\VK\VK;
class VkApi extends VK
{
/**
* @param string $app_id
* @param string $secret
* @param string $version
* @param string $lang
* @param int $https
*/
public function __construct($app_id, $secret, $version = '5.24', $lang = 'ru', $https = 1)
{
parent::__construct($app_id, $secret, $version, $lang, $https);
}
}
<?php
namespace PrestoHeads\VkBotBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpFoundation\JsonResponse;
class DefaultController extends Controller
{
/**
* @Route("/vk/bot/get_token")
*/
public function getTokenAction()
{
$time = microtime(true);
/** @var \PrestoHeads\VkBotBundle\VkAuthorize\VkAuthorize $auth */
$auth = $this->get('vk_bot.authorize');
$bot = $this->container->getParameter('vk_bot.bot');
$token = $auth->getAccessToken($bot['login'],$bot['password'],'desktop');
$time = microtime(true) - $time;
return new JsonResponse([
'success'=>true,
'token' => $token,
'time_execute'=> $time
]);
}
}
<?php
namespace PrestoHeads\VkBotBundle\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class VkAuthorizeCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('vk_bot.authorize')) {
return;
}
$definition = $container->getDefinition(
'vk_bot.authorize'
);
$adapters = $container->findTaggedServiceIds(
'vk_bot.authorize.adapter'
);
foreach ($adapters as $id => $attributes) {
$definition->addMethodCall(
'addAdapter',
array(new Reference($id))
);
}
$types = $container->findTaggedServiceIds(
'vk_bot.authorize.availableAuthType'
);
foreach ($types as $id => $attributes) {
$definition->addMethodCall(
'addAvailableAuthType',
array(new Reference($id))
);
}
}
}
<?php
namespace PrestoHeads\VkBotBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* This is the class that validates and merges configuration from your app/config files
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class}
*/
class Configuration implements ConfigurationInterface
{
/**
* {@inheritdoc}
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('presto_heads_vk_bot');
// Here you should define the parameters that are allowed to
// configure your bundle. See the documentation linked above for
// more information on that topic.
return $treeBuilder;
}
}
<?php
namespace PrestoHeads\VkBotBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
/**
* This is the class that loads and manages your bundle configuration
*
* To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
*/
class PrestoHeadsVkBotExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
}
}
<?php
namespace PrestoHeads\VkBotBundle;
use PrestoHeads\VkBotBundle\DependencyInjection\CompilerPass\VkAuthorizeCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class PrestoHeadsVkBotBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new VkAuthorizeCompilerPass());
}
}
parameters:
vk_bot.authorize.class: PrestoHeads\VkBotBundle\VkAuthorize\VkAuthorize
vk_bot.authorize.desktop.class: PrestoHeads\VkBotBundle\VkAuthorize\Adapter\DesktopApplicationAuthorize
vk_bot.api.class: PrestoHeads\VkBotBundle\Api\VkApi
vk_bot.app.id: ~
vk_bot.app.secret: ~
vk_bot.authorize.desktop.permissions: 6274559
vk_bot.authorize.desktop.offline_token: true
vk_bot.bot:
login: ~
password: ~
services:
vk_bot.api:
class: %vk_bot.api.class%
arguments: [%vk_bot.app.id%, %vk_bot.app.secret%, '5.24', 'ru', 1]
vk_bot.authorize:
class: %vk_bot.authorize.class%
vk_bot.authorize.desktop:
class: %vk_bot.authorize.desktop.class%
arguments: [%vk_bot.app.id%, %vk_bot.app.secret%, %vk_bot.authorize.desktop.permissions%, %vk_bot.authorize.desktop.offline_token%]
tags:
- { name: vk_bot.authorize.adapter }
- { name: vk_bot.authorize.availableAuthType }
vk_bot.api.group.wall:
class: PrestoHeads\VkBotBundle\Api\Group\Wall
arguments: [@vk_bot.api, @vk_bot.authorize, %vk_bot.bot%]
vk_bot.api.group.board:
class: PrestoHeads\VkBotBundle\Api\Group\Board
arguments: [@vk_bot.api, @vk_bot.authorize, %vk_bot.bot%]
vk_bot.api.group.photo:
class: PrestoHeads\VkBotBundle\Api\Group\Photo
arguments: [@vk_bot.api, @vk_bot.authorize, %vk_bot.bot%]
vk_bot.api.group.video:
class: PrestoHeads\VkBotBundle\Api\Group\Video
arguments: [@vk_bot.api, @vk_bot.authorize, %vk_bot.bot%]
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>Symfony2 is great</source>
<target>J'aime Symfony2</target>
</trans-unit>
</body>
</file>
</xliff>
<?php
namespace PrestoHeads\VkBotBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class DefaultControllerTest extends WebTestCase
{
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET', '/hello/Fabien');
$this->assertTrue($crawler->filter('html:contains("Hello Fabien")')->count() > 0);
}
}
<?php
namespace PrestoHeads\VkBotBundle\VkAuthorize\Adapter;
use DOMElement;
use PrestoHeads\VkBotBundle\VkAuthorize\Adapter\Exception\CurlException;
use PrestoHeads\VkBotBundle\VkAuthorize\Adapter\Exception\LoginException;
use PrestoHeads\VkBotBundle\VkAuthorize\AdapterInterface;
use Symfony\Component\DomCrawler\Crawler;
class DesktopApplicationAuthorize implements AdapterInterface
{
protected $app_id;
protected $app_secret;
protected $cookies;
protected $tokens = [];
/**
* VK oauth authorize url
* @link https://vk.com/dev/auth_mobile
* @var string
*/
protected $oauthUrl = 'https://oauth.vk.com/authorize';
/**
* VK request permissions, default full access
* @link https://vk.com/dev/permissions
* @var int
*/
protected $permissions = 6274559;
public function __construct($app_id, $app_secret, $permissions = 6274559, $isOfflineToken = true)
{
$this->app_id = $app_id;
$this->app_secret = $app_secret;
$this->cookies = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('desktop_app_');
if (!$isOfflineToken) {
$permissions = (($permissions & 65536) == 65536) ? $permissions - 65536 : $permissions;
}
$this->permissions = $permissions;
$o = new \PrestoHeads\VkBotBundle\VkAuthorize\VkAuthorize();
}
/**
* @return string
*/
public function getApplicationType()
{
return 'desktop';
}
/**
* @param $login
* @param $password
* @return string
* @throws LoginException
* @throws CurlException
*/
public function getAccessToken($login, $password)
{
$url = $this->getOAuthUrl();
$hash = md5($login.$password);
if (!isset($this->tokens[$hash])) {
$this->tokens[$hash] = $this->parseAccessToken(
$this->grantAccess(
$this->login($url, $login, $password)
)
);
}
return $this->tokens[$hash];
}
/**
* @param $url
* @param $login
* @param $password
* @return $this
* @throws CurlException
* @throws LoginException
*/
protected function login($url, $login, $password)
{
$result = $this->curlExec($url);
$page = new Crawler();
$page->addHtmlContent($result['body']);
$form = $page->filter('#mcont > div > div.form_item.fi_fat > form');
if ($form->count() != 1) {
throw new LoginException("On login action not found form or found more than one form.");
}
$post = [];
/** @var DOMElement $input */
foreach ($form->filter('input') as $input) {
if ($input->getAttribute('type') == 'submit') {
continue;
}
$post[$input->getAttribute('name')] = $input->getAttribute('value');
}
$post['email'] = $login;
$post['pass'] = $password;
$result = $this->curlExec($form->attr('action'), [], $result['last_url'], $post);
if ($result['http_code'] != 200) {
throw new LoginException("Login error, http_code: {$result['http_code']}");
}
return $result;
}
/**
* @param $curlResult
* @return array
* @throws CurlException
* @throws LoginException
*/
protected function grantAccess($curlResult)
{
$page = new Crawler();
$page->addHtmlContent($curlResult['body']);
$grantForm = $page->filter('#mcont > div > div.form_item > form');
if ($grantForm->count() != 1) {
throw new LoginException("On login action not found form or found more than one form.");
}
$post = [];
/** @var DOMElement $input */
foreach ($grantForm->filter('input') as $input) {
if ($input->getAttribute('type') == 'submit') {
continue;
}
$post[$input->getAttribute('name')] = $input->getAttribute('value');
}
$result = $this->curlExec($grantForm->attr('action'), [], $curlResult['last_url'], $post);
return $result;
}
/**
* @param $curlResult
* @return string
* @throws LoginException
*/
protected function parseAccessToken($curlResult)
{
$pattern = '/^Location:(.*)access_token=(?P<token>\w+)\&(.*)$/m';
$matches = [];
$location = null;
preg_match($pattern, $curlResult['header'], $matches);
if (!isset($matches['token'])) {
throw new LoginException("Not found access token. HEADERS: {$curlResult['header']}");
}
return $matches['token'];
}
protected function getOAuthUrl()
{
$queryString = http_build_query([
'client_id' => $this->app_id,
'scope' => $this->permissions,
'redirect_uri' => 'https://oauth.vk.com/blank.html',
'display' => 'mobile',
'v' => '5.28',
'response_type' => 'token',
'revoke' => '1'
]);
return sprintf("%s?%s", $this->oauthUrl, $queryString);
}
protected function curlExec($url, array $headers = [], $referer = '', array $post = null, $followRedirect = true, $userAgent = null)
{
$ch = curl_init($url);
if (is_null($userAgent)) {
$userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A';
}
curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
curl_setopt($ch, CURLOPT_REFERER, $referer);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_COOKIEFILE, $this->cookies);
curl_setopt($ch, CURLOPT_COOKIEJAR, $this->cookies);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $followRedirect);
curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_COOKIESESSION, true);
if (!is_null($post)) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post));
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new CurlException(curl_error($ch));
}
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$result = [];
$result['header'] = substr($response, 0, $header_size);
$result['body'] = substr($response, $header_size);
$result['http_code'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$result['last_url'] = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
curl_close($ch);
return $result;
}
}
<?php
namespace PrestoHeads\VkBotBundle\VkAuthorize;
interface AdapterInterface
{
/**
* @return string
*/
public function getApplicationType();
/**
* @param $login
* @param $password
* @return string
*/
public function getAccessToken($login, $password);
}
<?php
namespace PrestoHeads\VkBotBundle\VkAuthorize\Adapter\Exception;
class CurlException extends \Exception
{
}
<?php
namespace PrestoHeads\VkBotBundle\VkAuthorize\Adapter\Exception;
class LoginException extends \Exception
{
}
<?php
namespace PrestoHeads\VkBotBundle\VkAuthorize;
class VkAuthorize
{
/** @var AdapterInterface[] */
protected $adapters = [];
protected $availableAuthTypes = [];
public function addAdapter(AdapterInterface $adapter)
{
$this->adapters[] = $adapter;
}
public function addAvailableAuthType(AdapterInterface $adapter)
{
$this->availableAuthTypes[] = $adapter->getApplicationType();
}
public function __construct(array $availableAuthTypes = [])
{
$this->availableAuthTypes = $availableAuthTypes;
}
public function getAccessToken($login, $password, $type)
{
if (!in_array($type,$this->getAvailableAuthTypes())) {
throw new \Exception('Unknown authorize type. Available types: '.implode(', ',$this->getAvailableAuthTypes()));
}
foreach ($this->adapters as $adapter) {
if ($adapter->getApplicationType() == $type) {
return $adapter->getAccessToken($login, $password);
}
}
return null;
}
/**
* @return array
*/
public function getAvailableAuthTypes()
{
return $this->availableAuthTypes;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment