This commit is contained in:
f4ilji
2024-10-28 12:57:16 +05:00
parent e4678fc0d2
commit 6d516d1750
233 changed files with 6312 additions and 1314 deletions
+128 -8
View File
@@ -2,36 +2,140 @@
namespace App\Services\VK\Album;
use App\Services\VK\VkAuthService;
use CURLFile;
use GuzzleHttp\Client;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Log;
use VK\Client\VKApiClient;
class VkAlbumService
{
private VKApiClient $vk;
private VkAuthService $vkAuthService;
private string $wallToken;
private string $serviceToken;
private string $publicId;
private string $publicDomain;
public function __construct(VKApiClient $vk) {
$this->vk = $vk;
$this->wallToken = env('WALL_ACCESS_VK_TOKEN');
$this->vkAuthService = new VkAuthService();
$this->serviceToken = env('SERVICE_ACCESS_VK_KEY');
$this->publicId = env('PUBLIC_ID');
$this->publicDomain = env('PUBLIC_DOMAIN');
}
public function getServerForUploadImages()
public function getServerForUploadImages($album_id, $group_id)
{
return $this->vk->photos()->getUploadServer($this->wallToken, array(
''
));
return $this->vk->photos()->getUploadServer(
$this->vkAuthService->getToken()->access_token,
array(
'album_id' => $album_id,
'group_id' => $group_id,
)
);
}
public function uploadImagesToUploadServer($uploadUrl, $images)
{
// Массив для хранения локальных путей к загруженным изображениям
$localFiles = [];
// Загрузка изображений из URL
foreach ($images as $imageUrl) {
$localFile = tempnam(sys_get_temp_dir(), 'img_') . '.webp';
file_put_contents($localFile, file_get_contents($imageUrl));
$localFiles[] = $localFile;
}
// Подготовка данных для отправки
$postFields = [];
foreach ($localFiles as $index => $localFile) {
// Добавляем файл в массив
$postFields['file' . ($index + 1)] = new CURLFile($localFile);
}
// Инициализация cURL
$ch = curl_init();
// Установка параметров cURL
curl_setopt($ch, CURLOPT_URL, $uploadUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
// Выполнение запроса
$response = curl_exec($ch);
curl_close($ch);
// Проверка на ошибки
if (curl_errno($ch)) {
return 'Ошибка cURL: ' . curl_error($ch);
}
// Закрытие cURL
// Обработка ответа
$responseData = json_decode($response, true);
// Удаление временных файлов
foreach ($localFiles as $localFile) {
unlink($localFile);
}
return $responseData;
}
public function saveImagesToUploadServer($albumId, $server, $photosList, $hash)
{
// URL для запроса
$url = 'https://api.vk.com/method/photos.save';
// Подготовка данных для отправки
$postFields = [
'album_id' => $albumId,
'server' => $server,
'group_id' => env('PUBLIC_ID'),
'photos_list' => $photosList, // Преобразуем массив в JSON-строку
'hash' => $hash,
'access_token' => $this->vkAuthService->getToken()->access_token,
'v' => '5.131', // Версия API
];
// Инициализация cURL
$ch = curl_init();
// Установка параметров cURL
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
// Выполнение запроса
$response = curl_exec($ch);
// Проверка на ошибки
if (curl_errno($ch)) {
return 'Ошибка cURL: ' . curl_error($ch);
}
// Закрытие cURL
curl_close($ch);
// Обработка ответа
$responseData = json_decode($response, true);
return $responseData;
}
public function createAlbum(string $title)
{
try {
return $this->vk->photos()->createAlbum($this->wallToken, array(
return $this->vk->photos()->createAlbum($this->vkAuthService->getToken()->access_token, array(
'title' => $title,
'group_id' => $this->publicId,
'privacy' => 0,
@@ -46,4 +150,20 @@ class VkAlbumService
}
}
public function deleteAlbum(int $album_id, int $group_id)
{
try {
return $this->vk->photos()->deleteAlbum($this->vkAuthService->getToken()->access_token, array(
'album_id' => $album_id,
'group_id' => $group_id,
));
} catch (\Exception $e) {
Log::error('Ошибка при удалении альбома: ' . $e->getMessage());
return [
'success' => false,
'message' => 'Не удалось удалить альбом: ' . $e->getMessage(),
];
}
}
}
+220
View File
@@ -0,0 +1,220 @@
<?php
namespace App\Services\VK;
use App\Services\VK\Album\VkAlbumService;
use App\Services\VK\Wall\VkWallService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use VK\Client\VKApiClient;
class VkAuthService
{
public function getToken()
{
try {
// Получаем последний токен
$token = DB::table('vk_tokens')->latest()->first();
// Проверяем, существует ли токен и является ли он валидным
if ($token && !$this->isTokenValid($token)) {
// Если токен не валиден, обновляем его
return $this->refresh();
}
return $token;
} catch (\Exception $e) {
// Обработка ошибок, например, логирование
Log::error('Ошибка при получении токена: ' . $e->getMessage());
return null; // Или выбросьте исключение, если это необходимо
}
}
public function redirectToProvider()
{
$state = bin2hex(random_bytes(16)); // Генерация случайной строки состояния
session(['vk_state' => $state]);
$code_verifier = $this->generateCodeVerifier();
$code_challenge = $this->generateCodeChallenge($code_verifier);
session(['vk_code_verifier' => $code_verifier]);
$url = 'https://id.vk.com/authorize?' . http_build_query([
'response_type' => 'code',
'client_id' => env('VK_APP_ID'),
'redirect_uri' => env('VK_REDIRECT_URI'),
'state' => $state,
'scope' => 'photos wall', // Укажите необходимые права доступа
'code_challenge' => $code_challenge, // Добавьте код, если используете PKCE
'code_challenge_method' => 's256',
]);
return redirect($url);
}
public function handleProviderCallback(Request $request)
{
$this->validateState($request);
$codeVerifier = session('vk_code_verifier');
$tokenData = $this->exchangeCodeForTokens($request, $codeVerifier);
if (isset($tokenData->error)) {
return response()->json(['error' => $tokenData->error_description], 400);
}
return $this->storeTokenData($tokenData, $request);
}
public function refresh()
{
$token = DB::table('vk_tokens')->latest()->first();
$newTokenData = $this->refreshToken($token->refresh_token, bin2hex(random_bytes(16)), $token->device_id);
return $this->storeRefreshTokenData($newTokenData['data']);
}
public function logout()
{
session()->forget('vk_state');
session()->forget('vk_code_verifier');
return redirect('/'); // Перенаправление на главную страницу
}
private function exchangeCodeForTokens(Request $request, $codeVerifier)
{
return Http::asForm()->post('https://id.vk.com/oauth2/auth', [
'grant_type' => 'authorization_code',
'code_verifier' => $codeVerifier,
'redirect_uri' => env('VK_REDIRECT_URI_AFTER_AUTH'),
'code' => $request->code,
'client_id' => env('VK_APP_ID'),
'device_id' => $request->device_id,
'state' => $request->state,
'scope' => 'photos wall',
])->object();
}
private function refreshToken($refresh_token, $state, $device_id)
{
$response = Http::asForm()->post('https://id.vk.com/oauth2/auth', [
'grant_type' => 'refresh_token',
'refresh_token' => $refresh_token,
'client_id' => env('VK_APP_ID'),
'device_id' => $device_id,
'state' => $state,
'scope' => 'photos wall',
]);
if ($response->failed()) {
// Обработка ошибок
return [
'success' => false,
'error' => $response->json(), // Возвращаем детали ошибки
];
}
return [
'success' => true,
'data' => $response->object(), // Успешный ответ
];
}
private function storeTokenData($tokenData, Request $request)
{
try {
DB::table('vk_tokens')->updateOrInsert(
['user_id' => $tokenData->user_id],
[
'user_id' => $tokenData->user_id,
'access_token' => $tokenData->access_token,
'refresh_token' => $tokenData->refresh_token,
'id_token' => $tokenData->id_token,
'state' => $tokenData->state,
'scope' => $tokenData->scope,
'device_id' => $request->device_id,
'token_expire' => Carbon::createFromTimestamp(Carbon::now()->timestamp + $tokenData->expires_in),
'updated_at' => now(),
]
);
} catch (\Exception $e) {
Log::error('Ошибка при обновлении или вставке токена: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Произошла ошибка при обновлении или создания токена.',
'error_message' => $e->getMessage()
], 500);
}
return redirect('/'); // Перенаправление после успешной авторизации
}
private function storeRefreshTokenData($tokenData)
{
try {
// Обновляем запись и получаем количество затронутых строк
$updatedRows = DB::table('vk_tokens')->where('user_id', $tokenData->user_id)->update(
[
'access_token' => $tokenData->access_token,
'refresh_token' => $tokenData->refresh_token,
'state' => $tokenData->state,
'scope' => $tokenData->scope,
'token_expire' => Carbon::createFromTimestamp(Carbon::now()->timestamp + $tokenData->expires_in),
'updated_at' => now(),
]
);
// Если обновление прошло успешно, получаем обновленную запись
if ($updatedRows > 0) {
return DB::table('vk_tokens')->where('user_id', $tokenData->user_id)->first();
}
// Если запись не найдена, можно вернуть null или выбросить исключение
return response()->json([
'success' => false,
'message' => 'Запись не найдена для обновления.',
], 404);
} catch (\Exception $e) {
Log::error('Ошибка при обновлении токена: ' . $e->getMessage());
return response()->json([
'success' => false,
'message' => 'Произошла ошибка при обновлении токена.',
'error_message' => $e->getMessage()
], 500);
}
}
private function generateCodeVerifier($length = 128)
{
return bin2hex(random_bytes($length / 2));
}
private function generateCodeChallenge($code_verifier)
{
return rtrim(strtr(base64_encode(hash('sha256', $code_verifier, true)), '+/', '-_'), '=');
}
private function validateState(Request $request)
{
if ($request->input('state') !== session('vk_state')) {
abort(403, 'Invalid state');
}
}
private function isTokenValid($token)
{
if (Carbon::now() > $token->token_expire) {
return false;
}
return true;
}
}
+64 -5
View File
@@ -10,9 +10,12 @@ class VkService
{
protected VkWallService $wallService;
protected VkAlbumService $albumService;
public function __construct(VKApiClient $vk) {
protected int $public_id;
public function __construct() {
$vk = new VKApiClient();
$this->wallService = new VkWallService($vk);
$this->albumService = new VkAlbumService($vk);
$this->public_id = env('PUBLIC_ID');
}
public function getPosts(int $count = 10)
@@ -20,14 +23,70 @@ class VkService
return $this->wallService->getPosts($count);
}
public function createPost(string $message, int $from_group = 1)
public function getPostById(int $id)
{
return $this->wallService->createPost($message, $from_group);
return $this->wallService->getPostById($id);
}
public function createAlbum(string $title)
public function createPost(string $title, string $message, array $images = [], int $publish_date = null)
{
return $this->albumService->createAlbum($title);
$from_group = 1;
$album_attachment = '';
if ($images !== []) {
$album = $this->createAlbum($title, $images);
$album_attachment = $this->createAlbumAttachmentParam($album['id']);
}
return $this->wallService->createPost($message, $from_group, $album_attachment, $publish_date);
}
public function updatePost(int $id, string $title, string $message, array $images = [], int $publish_date = null)
{
$from_group = 1;
$vk_post = $this->wallService->getPostById($id);
$album_attachment = '';
if ($vk_post['attachments']) {
if ($this->getAlbumAttachment($vk_post['attachments'])) {
$album = $this->getAlbumAttachment($vk_post['attachments']);
$this->albumService->deleteAlbum($album['album']['id'], $this->public_id);
}
}
if ($images !== []) {
$album = $this->createAlbum($title, $images);
$album_attachment = $this->createAlbumAttachmentParam($album['id']);
}
return $this->wallService->updatePost($id, $message, $from_group, $album_attachment, $publish_date);
}
public function createAlbum(string $title, $images)
{
$album = $this->albumService->createAlbum($title);
$uploadServer = $this->albumService->getServerForUploadImages($album['id'], env('PUBLIC_ID'));
foreach (array_chunk($images, 4) as $images_slice) {
$images_data = $this->albumService->uploadImagesToUploadServer($uploadServer['upload_url'], $images_slice);
$this->albumService->saveImagesToUploadServer(
$images_data['aid'],
$images_data['server'],
$images_data['photos_list'],
$images_data['hash']
);
}
return $album;
}
private function createAlbumAttachmentParam(int $attachmentId)
{
return $this->wallService->generateAttachmentsParams('album', $attachmentId);
}
private function getAlbumAttachment(array $attachments)
{
$data = collect($attachments);
$filteredAttachment = $data->filter(function($attachment) {
return $attachment['type'] === 'album';
});
return (isset($filteredAttachment[0]) ? $filteredAttachment[0] : null);
}
}
+92 -7
View File
@@ -2,6 +2,10 @@
namespace App\Services\VK\Wall;
use App\Services\VK\VkAuthService;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use VK\Client\VKApiClient;
@@ -19,6 +23,8 @@ class VkWallService
$this->serviceToken = env('SERVICE_ACCESS_VK_KEY');
$this->publicId = env('PUBLIC_ID');
$this->publicDomain = env('PUBLIC_DOMAIN');
$this->vkAuthService = new VkAuthService();
}
public function getPosts(int $count)
@@ -30,15 +36,40 @@ class VkWallService
));
}
public function createPost(string $message, int $from_group, string $attachments = '')
public function getPostById(int $id)
{
$post = $this->vk->wall()->getById($this->serviceToken, array(
'posts' => '-'. $this->publicId . '_' . $id,
));
return $post[0];
}
public function createPost(string $message, int $from_group, string $attachments = '', int $publish_date)
{
$params = [
'owner_id' => '-' . $this->publicId,
'from_group' => $from_group,
'message' => $message,
'attachments' => $attachments,
'access_token' => $this->wallToken,
'publish_date' => $publish_date,
'v' => '5.131',
];
try {
return $this->vk->wall()->post($this->wallToken, array(
'owner_id' => '-' . $this->publicId,
'from_group' => $from_group,
'message' => $message,
'attachments' => $attachments
));
$response = Http::asForm()->post('https://api.vk.com/method/wall.post', $params);
if ($response->successful() && isset($response['response'])) {
return [
'success' => true,
'post_id' => $response['response']['post_id'],
];
} else {
throw new \Exception('Ошибка API: ' . json_encode($response->json()));
}
} catch (\Exception $e) {
Log::error('Ошибка при создании поста: ' . $e->getMessage());
return [
@@ -47,4 +78,58 @@ class VkWallService
];
}
}
public function updatePost(int $post_id, string $message = '', int $from_group = 1, string $attachments = '', int $publish_date)
{
if (empty($message) && empty($attachments)) {
return [
'success' => false,
'message' => 'Необходимо указать либо сообщение, либо вложения.',
];
}
$params = [
'owner_id' => '-' . $this->publicId,
'post_id' => $post_id,
'message' => $message,
'attachments' => $attachments,
'access_token' => $this->wallToken,
'v' => '5.131',
];
try {
return $this->vk->wall()->edit(
$this->vkAuthService->getToken()->access_token,
array(
'owner_id' => '-' . $this->publicId,
'post_id' => $post_id,
'message' => $message,
'attachments' => $attachments,
'publish_date' => $publish_date,
),
);
} catch (\Exception $e) {
Log::error('Ошибка при обновлении новости SDK: ' . $e->getMessage());
return [
'success' => false,
'message' => 'Не удалось обновить новость SDK: ' . $e->getMessage(),
];
}
}
public function generateAttachmentsParams(string $attachmentType, int $attachmentId)
{
$pubic_id = env('PUBLIC_ID');
switch ($attachmentType) {
case 'album': {
return "album-{$pubic_id}_{$attachmentId}";
}
case 'doc': {
}
}
}
}