From 0b60c4b9080ddb44ea54392779674ea6f12891c3 Mon Sep 17 00:00:00 2001 From: Fasutoa Date: Wed, 18 Jun 2025 20:30:41 +0500 Subject: [PATCH] add_vk_module --- .DS_Store | Bin 6148 -> 6148 bytes app/.DS_Store | Bin 8196 -> 8196 bytes .../PostResource/Pages/CreatePost.php | 7 + app/Jobs/CreateVkPostJob.php | 318 ++++++++++++++++ app/Jobs/UpdateVkPostJob.php | 340 ++++++++++++++++++ app/Observers/PostObserver.php | 2 + .../Filament/Domain/Posts/TgPostPublisher.php | 228 ++++++++++++ .../Filament/Domain/Posts/VkPostPublisher.php | 186 ++++++++-- app/Services/VK/Album/VkAlbumService.php | 12 +- app/Services/VK/VkAuthService.php | 57 ++- app/Services/VK/VkService.php | 183 +++++++++- app/Services/VK/Wall/VkWallService.php | 53 ++- composer.json | 1 + database/.DS_Store | Bin 10244 -> 10244 bytes ..._25_195956_create_telegraph_bots_table.php | 23 ++ ...25_195957_create_telegraph_chats_table.php | 26 ++ ..._27_203434_create_posts_tg_posts_table.php | 30 ++ ...31_add_img_ids_in_posts_tg_posts_table.php | 28 ++ 18 files changed, 1432 insertions(+), 62 deletions(-) create mode 100644 app/Jobs/CreateVkPostJob.php create mode 100644 app/Jobs/UpdateVkPostJob.php create mode 100644 app/Services/Filament/Domain/Posts/TgPostPublisher.php create mode 100644 database/migrations/2025_04_25_195956_create_telegraph_bots_table.php create mode 100644 database/migrations/2025_04_25_195957_create_telegraph_chats_table.php create mode 100644 database/migrations/2025_04_27_203434_create_posts_tg_posts_table.php create mode 100644 database/migrations/2025_05_04_155131_add_img_ids_in_posts_tg_posts_table.php diff --git a/.DS_Store b/.DS_Store index c53bc002bb436c6598cb600b5f82be0d61d91cb7..665f35e172496a8c97c6b7f7690a67edaff7346c 100644 GIT binary patch delta 310 zcmZoMXfc=|#>B!ku~2NHo+2a1#(>?7ivyUM7&#{MFzMDaGB7Z3Fr+XfGL!&u5)c$S)5rNh~QXc1kRY2Ju4j z^K+75?8Kz7%+&ID0TJi?ypqJsywoDFhRl>yppuyI%)FHRa;N;#yp+gdu;yTh3OLfC$r4vVy3xFo7p+|Ie?+E*^&7>^JIPzM-HGt NOd#ExBSh9P0{|`+PALEY delta 77 zcmZoMXfc=|#>B)qu~2NHo+2aL#(>?7jBJy6SacZ~CJVAEOlD@2+?>TamuX|ecc#tk f9Q+(W#hV2=zB5ne7jfiZ00Kq^2A0hcB5Rld-e(b) diff --git a/app/.DS_Store b/app/.DS_Store index 104b1d57dccfe769b01a72c780eacc2a51e28f93..26feb45eef6646e752d47a6cd0b02f1b24fd8d10 100644 GIT binary patch delta 87 zcmZp1XmOa}aFU^hRb>SP{)$(!W`S2A&?6es5-<>%)xOx`10y?MV#0V7<9akI48 W8^(zZ*EX|Dd}EpXLDU{Y>M{UFCLl%t delta 319 zcmZp1XmOa}jIU^hRb_GBJ`$@Q!Zt_*n$r3|?Y#X0GQ!O8i#1q@)o%nhWFWOMUf zT#|C~lYpWe#*KR`nSLB~L|2hQKt(|YvJC=2?VINbtYVrRE1X}?#^B44&yd29%8-NZ z6n&6IDDudT*>UK?%-Jf(u&6;e2bUUzgV=ypFexxhelIMx`Mz*0createSeo($this->record); $this->sendNotifications(); $this->publishToVk(); + $this->publishToTg(); } protected function handleSlides(): void @@ -74,6 +76,11 @@ class CreatePost extends CreateRecord (new VkPostPublisher())->publish($this->publicationAgreements, $this->record); } + protected function publishToTg(): void + { + (new TgPostPublisher())->publish($this->publicationAgreements, $this->record); + } + diff --git a/app/Jobs/CreateVkPostJob.php b/app/Jobs/CreateVkPostJob.php new file mode 100644 index 0000000..2402aa5 --- /dev/null +++ b/app/Jobs/CreateVkPostJob.php @@ -0,0 +1,318 @@ +post_id = $post_id; + $this->title = $title; + $this->message = $message; + $this->images = $images; + $this->videos = $videos; + $this->documents = $documents; + $this->publish_date = $publish_date; + $this->public_id = env('PUBLIC_ID'); + } + + public function handle() + { + $vk = new VKApiClient(); + $wallService = new VkWallService($vk); + + $from_group = 1; + + $doc_attachments = !empty($this->documents) ? $this->prepareDocuments($this->documents) : ''; + $doc_list = $doc_attachments ? explode(',', $doc_attachments) : []; + $doc_count = count($doc_list); + + if ($doc_count >= 10) { + $selected_attachments = array_slice($doc_list, 0, 10); + $image_list = []; + } else { + $video_attachments = !empty($this->videos) ? $this->prepareVideos($this->videos) : ''; + $video_list = $video_attachments ? explode(',', $video_attachments) : []; + $available_slots = 10 - $doc_count; + $attached_videos = array_slice($video_list, 0, min(count($video_list), $available_slots)); + $remaining_slots = $available_slots - count($attached_videos); + + if ($remaining_slots > 0 && !empty($this->images)) { + // Подготовка только тех изображений, которые поместятся + $images_to_attach = array_slice($this->images, 0, $remaining_slots); + $image_attachments = $this->prepareWallPhotos($images_to_attach); + $image_list = $image_attachments ? explode(',', $image_attachments) : []; + } else { + $image_list = []; + } + + // Собираем все вложения с учетом приоритетов + $selected_attachments = array_merge($doc_list, $attached_videos, $image_list); + } + + // Проверяем, все ли изображения прикреплены + if (count($this->images) > count($image_list)) { + // Если не все изображения вошли, создаем альбом + $album = $this->createAlbum($this->title, $this->images); + if (isset($album['id'])) { + $albumLink = "https://vk.com/album-{$this->public_id}_{$album['id']}"; + $this->message .= "\n\n[{$albumLink}|Ссылка на все фотографии]"; + } else { + Log::error('Не удалось создать альбом'); + } + } + + $attachmentString = implode(',', $selected_attachments); + + $vk_post = $wallService->createPost($this->message, $from_group, $attachmentString, $this->publish_date); + + DB::table('posts_vk_posts')->insert( + [ + 'post_id' => $this->post_id, + 'vk_post_id' => $vk_post['post_id'], + 'unchange_time_after' => Carbon::now()->addWeek(), + ] + ); + } + + private function prepareWallPhotos(array $images): string + { + $baseUrl = config('app.url'); + try { + $imagePaths = array_map(function($img) use ($baseUrl) { + return $baseUrl . $img; + }, $images); + + $photos = $this->uploadWallPhotos($imagePaths, $this->public_id); + + return implode(',', array_map(function($photo) { + return "photo{$photo['owner_id']}_{$photo['id']}"; + }, $photos)); + } catch (\Exception $e) { + Log::error("Ошибка при подготовке фотографий: " . $e->getMessage()); + return ''; + } + } + + private function prepareVideos(array $videos): string + { + $baseUrl = config('app.url'); + $videoPaths = array_map(function($vid) use ($baseUrl) { + return $baseUrl . $vid; + }, $videos); + $uploadedVideos = $this->uploadVideos($videoPaths, $this->public_id); + return implode(',', $uploadedVideos); + } + + private function prepareDocuments(array $documents): string + { + $baseUrl = config('app.url'); + try { + $documentPaths = array_map(function($doc) use ($baseUrl) { + return $baseUrl . $doc; + }, $documents); + Log::info(json_encode($documentPaths)); + $uploadedDocs = $this->uploadDocuments($documentPaths, $this->public_id); + return implode(',', $uploadedDocs); + } catch (\Exception $e) { + Log::error("Ошибка при подготовке документов: " . $e->getMessage()); + return ''; + } + } + + private function uploadWallPhotos(array $imagePaths, ?int $groupId = null): array + { + $uploadedPhotos = []; + + foreach ($imagePaths as $imagePath) { + try { + $uploadServerResponse = Http::get('https://api.vk.com/method/photos.getWallUploadServer', [ + 'group_id' => $groupId, + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $uploadServerData = $uploadServerResponse->json(); + if (!isset($uploadServerData['response']['upload_url'])) { + throw new \Exception('Не удалось получить сервер для загрузки: ' . $imagePath); + } + $uploadUrl = $uploadServerData['response']['upload_url']; + + $uploadResponse = Http::attach( + 'photo', + file_get_contents($imagePath), + basename($imagePath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['photo']) || !isset($uploadData['server']) || !isset($uploadData['hash'])) { + throw new \Exception('Не удалось загрузить фотографию: ' . $imagePath); + } + + $saveResponse = Http::get('https://api.vk.com/method/photos.saveWallPhoto', [ + 'group_id' => $groupId, + 'photo' => $uploadData['photo'], + 'server' => $uploadData['server'], + 'hash' => $uploadData['hash'], + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + if (!isset($saveData['response'][0])) { + throw new \Exception('Не удалось сохранить фотографию: ' . $imagePath); + } + + $uploadedPhotos[] = $saveData['response'][0]; + Log::info("Фотография загружена. ID: " . $saveData['response'][0]['id']); + } catch (\Exception $e) { + Log::error("Ошибка при загрузке {$imagePath}: " . $e->getMessage()); + } + } + + return $uploadedPhotos; + } + + private function uploadVideos(array $videoPaths, ?int $groupId = null): array + { + $uploadedVideos = []; + + foreach ($videoPaths as $videoPath) { + try { + $saveResponse = Http::timeout(360)->get('https://api.vk.com/method/video.save', [ + 'group_id' => $groupId, + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + if (!isset($saveData['response']['upload_url'])) { + throw new \Exception('Не удалось получить сервер для загрузки видео: ' . $videoPath); + } + $uploadUrl = $saveData['response']['upload_url']; + + $uploadResponse = Http::timeout(360)->attach( + 'video_file', + file_get_contents($videoPath), + basename($videoPath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['video_id']) || !isset($uploadData['owner_id'])) { + throw new \Exception('Не удалось загрузить видео: ' . $videoPath); + } + + $attachment = "video{$uploadData['owner_id']}_{$uploadData['video_id']}"; + $uploadedVideos[] = $attachment; + } catch (\Exception $e) { + Log::error("Ошибка при загрузке видео {$videoPath}: " . $e->getMessage()); + } + } + + return $uploadedVideos; + } + + private function uploadDocuments(array $documentPaths, ?int $groupId = null): array + { + $uploadedDocs = []; + + foreach ($documentPaths as $docPath) { + try { + // Получение сервера для загрузки документов на стену группы + $uploadServerResponse = Http::get('https://api.vk.com/method/docs.getWallUploadServer', [ + 'group_id' => $groupId, + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $uploadServerData = $uploadServerResponse->json(); + Log::info($uploadServerData); + if (!isset($uploadServerData['response']['upload_url'])) { + throw new \Exception('Не удалось получить сервер для загрузки документа: ' . $docPath); + } + $uploadUrl = $uploadServerData['response']['upload_url']; + + // Загрузка документа на сервер + $uploadResponse = Http::attach( + 'file', + file_get_contents($docPath), + basename($docPath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['file'])) { + throw new \Exception('Не удалось загрузить документ: ' . $docPath); + } + + // Сохранение документа + $saveResponse = Http::get('https://api.vk.com/method/docs.save', [ + 'file' => $uploadData['file'], + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + + // Проверка наличия данных о документе + if (!isset($saveData['response']['doc'])) { + throw new \Exception('Не удалось сохранить документ: ' . $docPath); + } + + // Получение данных о документе + $doc = $saveData['response']['doc']; + $attachment = "doc{$doc['owner_id']}_{$doc['id']}"; + $uploadedDocs[] = $attachment; + } catch (\Exception $e) { + Log::error("Ошибка при загрузке документа {$docPath}: " . $e->getMessage()); + } + } + + return $uploadedDocs; + } + + public function createAlbum(string $title, $images) + { + $vk = new VKApiClient(); + + $album = (new VkAlbumService($vk))->createAlbum($title); + $uploadServer = (new VkAlbumService($vk))->getServerForUploadImages($album['id'], env('PUBLIC_ID')); + foreach (array_chunk($images, 4) as $images_slice) { + $images_data = (new VkAlbumService($vk))->uploadImagesToUploadServer($uploadServer['upload_url'], $images_slice); + (new VkAlbumService($vk))->saveImagesToUploadServer( + $images_data['aid'], + $images_data['server'], + $images_data['photos_list'], + $images_data['hash'] + ); + } + return $album; + } +} diff --git a/app/Jobs/UpdateVkPostJob.php b/app/Jobs/UpdateVkPostJob.php new file mode 100644 index 0000000..16463fb --- /dev/null +++ b/app/Jobs/UpdateVkPostJob.php @@ -0,0 +1,340 @@ +post_id = $post_id; + $this->title = $title; + $this->message = $message; + $this->images = $images; + $this->videos = $videos; + $this->documents = $documents; + $this->publish_date = $publish_date; + $this->public_id = env('PUBLIC_ID'); + } + + public function handle() + { + $vk = new VKApiClient(); + $wallService = new VkWallService($vk); + $albumService = new VkAlbumService($vk); + + $postRelation = DB::table('posts_vk_posts')->select()->where('post_id', $this->post_id)->first(); + $this->post_id = $postRelation->vk_post_id; + + $vk_post = $wallService->getPostById($this->post_id); + + // доделать + if ($vk_post['attachments']) { + Log::info($vk_post); + if ($this->getAlbumAttachment($vk_post['attachments'])) { + Log::info('вход в аттачментс вк'); + $album = $this->getAlbumAttachment($vk_post['attachments']); + Log::info($album); + $albumService->deleteAlbum($album['album']['id'], $this->public_id); + } + } + + $from_group = 1; + + // Подготовка документов + $doc_attachments = !empty($this->documents) ? $this->prepareDocuments($this->documents) : ''; + $doc_list = $doc_attachments ? explode(',', $doc_attachments) : []; + $doc_count = count($doc_list); + + if ($doc_count >= 10) { + // Если документов 10 или больше, прикрепляем только первые 10 + $selected_attachments = array_slice($doc_list, 0, 10); + $image_list = []; + } else { + // Подготовка видео + $video_attachments = !empty($this->videos) ? $this->prepareVideos($this->videos) : ''; + $video_list = $video_attachments ? explode(',', $video_attachments) : []; + $available_slots = 10 - $doc_count; + $attached_videos = array_slice($video_list, 0, min(count($video_list), $available_slots)); + $remaining_slots = $available_slots - count($attached_videos); + + if ($remaining_slots > 0 && !empty($this->images)) { + // Подготовка только тех изображений, которые поместятся + $images_to_attach = array_slice($this->images, 0, $remaining_slots); + $image_attachments = $this->prepareWallPhotos($images_to_attach); + $image_list = $image_attachments ? explode(',', $image_attachments) : []; + } else { + $image_list = []; + } + + // Собираем все вложения с учетом приоритетов + $selected_attachments = array_merge($doc_list, $attached_videos, $image_list); + } + + // Проверяем, все ли изображения прикреплены + if (count($this->images) > count($image_list)) { + // Если не все изображения вошли, создаем альбом + $album = $this->createAlbum($this->title, $this->images); + if (isset($album['id'])) { + $albumLink = "https://vk.com/album-{$this->public_id}_{$album['id']}"; + $this->message .= "\n\n[{$albumLink}|Ссылка на все фотографии]"; + } else { + Log::error('Не удалось создать альбом'); + } + } + + $attachmentString = implode(',', $selected_attachments); + + $wallService->updatePost($this->post_id, $this->message, $from_group, $attachmentString, $this->publish_date); + } + + private function getAlbumAttachment(array $attachments) + { + $data = collect($attachments); + $filteredAttachment = $data->filter(function($attachment) { + return $attachment['type'] === 'album'; + }); + return (isset($filteredAttachment[0]) ? $filteredAttachment[0] : null); + } + + private function prepareWallPhotos(array $images): string + { + $baseUrl = config('app.url'); + try { + $imagePaths = array_map(function($img) use ($baseUrl) { + return $baseUrl . $img; + }, $images); + + $photos = $this->uploadWallPhotos($imagePaths, $this->public_id); + + return implode(',', array_map(function($photo) { + return "photo{$photo['owner_id']}_{$photo['id']}"; + }, $photos)); + } catch (\Exception $e) { + Log::error("Ошибка при подготовке фотографий: " . $e->getMessage()); + return ''; + } + } + + private function prepareVideos(array $videos): string + { + $baseUrl = config('app.url'); + $videoPaths = array_map(function($vid) use ($baseUrl) { + return $baseUrl . $vid; + }, $videos); + $uploadedVideos = $this->uploadVideos($videoPaths, $this->public_id); + return implode(',', $uploadedVideos); + } + + private function prepareDocuments(array $documents): string + { + $baseUrl = config('app.url'); + try { + $documentPaths = array_map(function($doc) use ($baseUrl) { + return $baseUrl . $doc; + }, $documents); + Log::info(json_encode($documentPaths)); + $uploadedDocs = $this->uploadDocuments($documentPaths, $this->public_id); + return implode(',', $uploadedDocs); + } catch (\Exception $e) { + Log::error("Ошибка при подготовке документов: " . $e->getMessage()); + return ''; + } + } + + private function uploadWallPhotos(array $imagePaths, ?int $groupId = null): array + { + $uploadedPhotos = []; + + foreach ($imagePaths as $imagePath) { + try { + $uploadServerResponse = Http::get('https://api.vk.com/method/photos.getWallUploadServer', [ + 'group_id' => $groupId, + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $uploadServerData = $uploadServerResponse->json(); + if (!isset($uploadServerData['response']['upload_url'])) { + throw new \Exception('Не удалось получить сервер для загрузки: ' . $imagePath); + } + $uploadUrl = $uploadServerData['response']['upload_url']; + + $uploadResponse = Http::attach( + 'photo', + file_get_contents($imagePath), + basename($imagePath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['photo']) || !isset($uploadData['server']) || !isset($uploadData['hash'])) { + throw new \Exception('Не удалось загрузить фотографию: ' . $imagePath); + } + + $saveResponse = Http::get('https://api.vk.com/method/photos.saveWallPhoto', [ + 'group_id' => $groupId, + 'photo' => $uploadData['photo'], + 'server' => $uploadData['server'], + 'hash' => $uploadData['hash'], + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + if (!isset($saveData['response'][0])) { + throw new \Exception('Не удалось сохранить фотографию: ' . $imagePath); + } + + $uploadedPhotos[] = $saveData['response'][0]; + Log::info("Фотография загружена. ID: " . $saveData['response'][0]['id']); + } catch (\Exception $e) { + Log::error("Ошибка при загрузке {$imagePath}: " . $e->getMessage()); + } + } + + return $uploadedPhotos; + } + + private function uploadVideos(array $videoPaths, ?int $groupId = null): array + { + $uploadedVideos = []; + + foreach ($videoPaths as $videoPath) { + try { + $saveResponse = Http::timeout(360)->get('https://api.vk.com/method/video.save', [ + 'group_id' => $groupId, + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + if (!isset($saveData['response']['upload_url'])) { + throw new \Exception('Не удалось получить сервер для загрузки видео: ' . $videoPath); + } + $uploadUrl = $saveData['response']['upload_url']; + + $uploadResponse = Http::timeout(360)->attach( + 'video_file', + file_get_contents($videoPath), + basename($videoPath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['video_id']) || !isset($uploadData['owner_id'])) { + throw new \Exception('Не удалось загрузить видео: ' . $videoPath); + } + + $attachment = "video{$uploadData['owner_id']}_{$uploadData['video_id']}"; + $uploadedVideos[] = $attachment; + } catch (\Exception $e) { + Log::error("Ошибка при загрузке видео {$videoPath}: " . $e->getMessage()); + } + } + + return $uploadedVideos; + } + + private function uploadDocuments(array $documentPaths, ?int $groupId = null): array + { + $uploadedDocs = []; + + foreach ($documentPaths as $docPath) { + try { + // Получение сервера для загрузки документов на стену группы + $uploadServerResponse = Http::get('https://api.vk.com/method/docs.getWallUploadServer', [ + 'group_id' => $groupId, + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $uploadServerData = $uploadServerResponse->json(); + Log::info($uploadServerData); + if (!isset($uploadServerData['response']['upload_url'])) { + throw new \Exception('Не удалось получить сервер для загрузки документа: ' . $docPath); + } + $uploadUrl = $uploadServerData['response']['upload_url']; + + // Загрузка документа на сервер + $uploadResponse = Http::attach( + 'file', + file_get_contents($docPath), + basename($docPath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['file'])) { + throw new \Exception('Не удалось загрузить документ: ' . $docPath); + } + + // Сохранение документа + $saveResponse = Http::get('https://api.vk.com/method/docs.save', [ + 'file' => $uploadData['file'], + 'access_token' => (new VkAuthService())->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + + // Проверка наличия данных о документе + if (!isset($saveData['response']['doc'])) { + throw new \Exception('Не удалось сохранить документ: ' . $docPath); + } + + // Получение данных о документе + $doc = $saveData['response']['doc']; + $attachment = "doc{$doc['owner_id']}_{$doc['id']}"; + $uploadedDocs[] = $attachment; + } catch (\Exception $e) { + Log::error("Ошибка при загрузке документа {$docPath}: " . $e->getMessage()); + } + } + + return $uploadedDocs; + } + + public function createAlbum(string $title, $images) + { + $vk = new VKApiClient(); + + $album = (new VkAlbumService($vk))->createAlbum($title); + $uploadServer = (new VkAlbumService($vk))->getServerForUploadImages($album['id'], env('PUBLIC_ID')); + foreach (array_chunk($images, 4) as $images_slice) { + $images_data = (new VkAlbumService($vk))->uploadImagesToUploadServer($uploadServer['upload_url'], $images_slice); + (new VkAlbumService($vk))->saveImagesToUploadServer( + $images_data['aid'], + $images_data['server'], + $images_data['photos_list'], + $images_data['hash'] + ); + } + return $album; + } +} + diff --git a/app/Observers/PostObserver.php b/app/Observers/PostObserver.php index 05beb9a..ff54f5e 100644 --- a/app/Observers/PostObserver.php +++ b/app/Observers/PostObserver.php @@ -3,6 +3,7 @@ namespace App\Observers; use App\Containers\Article\Models\Post; +use App\Services\VK\VkService; use App\Services\App\Cache\PostCacheService; class PostObserver @@ -36,6 +37,7 @@ class PostObserver public function deleted(Post $post) { $this->postCacheService->clearAllCacheByModel(); + app(VkService::class)->deletePost($post->id); } /** diff --git a/app/Services/Filament/Domain/Posts/TgPostPublisher.php b/app/Services/Filament/Domain/Posts/TgPostPublisher.php new file mode 100644 index 0000000..0a74d95 --- /dev/null +++ b/app/Services/Filament/Domain/Posts/TgPostPublisher.php @@ -0,0 +1,228 @@ +status === PostStatus::PUBLISHED) { + if ($settings['telegram']) { + $imagesFromPost = $post->images; + $imagesFromContent = $this->extractImagesFromContent($post->content); + $allImages = array_merge($imagesFromPost, $imagesFromContent); + $imageLinks = $this->generateImagesForTg($allImages); + + $text = $this->generateContentForTg($post, $allImages); + + $videos = $this->extractVideosFromContent($post->content); + $documents = $this->extractFilesFromContent($post->content); + + $publishDate = $post->publish_at > now() ? $post->publish_at->timestamp : null; + dispatch(new CreateTgPostJob($post->id, $post->title, $text, $imageLinks, $videos, $documents, $publishDate)); + } + } + } + + public function update(array $settings, Post $post): void + { + Log::info(json_encode($settings)); + if ($post->status === PostStatus::PUBLISHED) { + if ($settings['telegram']) { + $text = $this->generateContentForTg($post->title, $post->content, $post->slug, $post->images); + $images = $this->generateImagesForTg($post->images); + $publishDate = $post->publish_at > now() ? $post->publish_at->timestamp : null; + + dispatch(new UpdateTgPost($text, $images, $post->id, $publishDate)); + } + } + } + + private function generateContentForTg($post, $images): string + { + $message = ''; + $message .= "*{$post->title}*\n\n"; + + if (is_array($post->content)) { + $message .= $this->parseContentBlocks($post->content); + } elseif (is_string($post->content)) { + $contentBlocks = json_decode($post->content, true); + if (is_array($contentBlocks)) { + $message .= $this->parseContentBlocks($contentBlocks); + } else { + $message .= $this->convertToMarkdown($post->content); + } + } else { + $message .= $this->convertToMarkdown((string) $post->content); + } + + if (isset($post->authors) && is_array($post->authors) && !empty($post->authors)) { + $authors = array_map(function($author) { + return json_decode('"' . $author . '"'); + }, $post->authors); + $authorLabel = (count($authors) === 1) ? "Автор:" : "Авторы:"; + $message .= "\n\n$authorLabel " . implode(', ', $authors); + } + + if ($post->slug) { + $url = config('app.url') . "/posts/{$post->slug}"; + $linkText = "\n\n[Читать полностью]($url)"; + $linkLength = mb_strlen($linkText); + $maxLength = $images ? 1024 : 4096; + + if (mb_strlen($message) > $maxLength - $linkLength) { + $truncated = mb_substr($message, 0, $maxLength - $linkLength); + $lastSpace = mb_strrpos($truncated, ' '); + if ($lastSpace !== false) { + $truncated = mb_substr($truncated, 0, $lastSpace); + } + $message = $truncated . '...'; + $message .= $linkText; + } elseif (count($images) > 10) { + $message .= $linkText; + } + } + + return trim($message); + } + + /** + * Генерирует ссылки на изображения для Телеграмм. + * + * @param array $images + * @return array + */ + private function generateImagesForTg(array $images): array + { + return array_map(function ($file) { + return Storage::url($file); // Генерируем полный URL для изображения + }, $images); + } + + private function parseContentBlocks(array $blocks): string + { + $text = ''; + foreach ($blocks as $block) { + switch ($block['type'] ?? '') { + case 'heading': + if (isset($block['data']['content'])) { + $level = $block['data']['level'] ?? 1; // Уровень заголовка, по умолчанию 1 + $content = $this->convertToMarkdown($block['data']['content']); + $text .= str_repeat('#', $level) . ' ' . $content . "\n\n"; + } + break; + case 'paragraph': + if (isset($block['data']['content'])) { + $text .= $this->convertToMarkdown($block['data']['content']) . "\n\n"; + } + break; + case 'list': + if (isset($block['data']['items']) && is_array($block['data']['items'])) { + foreach ($block['data']['items'] as $item) { + $text .= "- " . $item . "\n"; + } + $text .= "\n"; + } + break; + } + } + return $text; + } + + private function extractImagesFromContent(array $blocks): array + { + $images = []; + foreach ($blocks as $block) { + if ($block['type'] === 'image' && isset($block['data']['url']) && is_array($block['data']['url'])) { + foreach ($block['data']['url'] as $imagePath) { + $images[] = $imagePath; + } + } + } + return $images; + } + + private function extractVideosFromContent(array $blocks): array + { + $videos = []; + foreach ($blocks as $block) { + if ($block['type'] === 'video' && isset($block['data']['path'])) { + $path = Storage::url($block['data']['path']); + $path = stripslashes($path); + $videos[] = $path; + } + } + return $videos; + } + + private function extractFilesFromContent(array $blocks): array + { + $files = []; + foreach ($blocks as $block) { + if ($block['type'] === 'files' && isset($block['data']['file']) && is_array($block['data']['file'])) { + foreach ($block['data']['file'] as $file) { + if (isset($file['path'])) { + $files[] = Storage::url($file['path']); + } + } + } + } + return $files; + } + + private function convertToMarkdown(string $text): string + { + // Декодируем HTML-сущности (например,   становится \u00A0) + $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8'); + + // Заменяем неразрывные пробелы на обычные + $text = str_replace("\xc2\xa0", " ", $text); // \xc2\xa0 — это \u00A0 в UTF-8 + + // Обрабатываем цитаты + $text = preg_replace_callback('/
(.*?)<\/blockquote>/s', function($match) { + $content = $match[1]; + // Удаляем

и заменяем

на \n + $content = preg_replace('/

(.*?)<\/p>/', "$1\n", $content); + // Разбиваем на строки и убираем пустые + $lines = array_filter(explode("\n", $content), function($line) { + return trim($line) !== ''; + }); + // Добавляем > перед каждой непустой строкой + $quoted = array_map(function($line) { + return '> ' . trim($line); + }, $lines); + return implode("\n", $quoted) . "\n"; + }, $text); + + // Удаляем

и заменяем

на \n\n для параграфов + $text = preg_replace('/

/', '', $text); + $text = preg_replace('/<\/p>/', "\n", $text); + + // Обрабатываем жирный текст и курсив, убирая лишние пробелы + $text = preg_replace_callback('/(.*?)<\/strong>/', function($match) { + return '*' . trim($match[1]) . '*'; + }, $text); + $text = preg_replace_callback('/(.*?)<\/em>/', function($match) { + return '_' . trim($match[1]) . '_'; + }, $text); + // Удаляем оставшиеся HTML-теги + $text = strip_tags($text); + // Убираем лишние пробелы и пустые строки в начале и конце + return trim($text); + } +} \ No newline at end of file diff --git a/app/Services/Filament/Domain/Posts/VkPostPublisher.php b/app/Services/Filament/Domain/Posts/VkPostPublisher.php index 452f15e..f9fa3f7 100644 --- a/app/Services/Filament/Domain/Posts/VkPostPublisher.php +++ b/app/Services/Filament/Domain/Posts/VkPostPublisher.php @@ -7,6 +7,9 @@ use App\Containers\Article\Models\Post; use App\Jobs\CreateVkPost; use App\Jobs\UpdateVkPost; use Illuminate\Support\Facades\Storage; +use App\Jobs\CreateVkPostJob; +use App\Jobs\UpdateVkPostJob; +use Illuminate\Support\Facades\Log; class VkPostPublisher { @@ -19,59 +22,187 @@ class VkPostPublisher */ public function publish(array $settings, Post $post): void { - if ($post->status->value === PostStatus::PUBLISHED->value) { + if ($post->status === PostStatus::PUBLISHED) { if ($settings['vk']) { - $text = $this->generateContentForVk($post->content); - $images = $this->generateImageLinksForVk($post->images); + $text = $this->generateContentForVk($post); + + $imagesFromPost = $post->images; + $imagesFromContent = $this->extractImagesFromContent($post->content); + $allImages = array_merge($imagesFromPost, $imagesFromContent); + $imageLinks = $this->generateImageLinksForVk($allImages); + $videos = $this->extractVideosFromContent($post->content); + $documents = $this->extractFilesFromContent($post->content); + $publishDate = $post->publish_at > now() ? $post->publish_at->timestamp : null; - dispatch(new CreateVkPost($post->title, $text, $images, $post->id, $publishDate)); + dispatch(new CreateVkPostJob( + $post->id, + $post->title, + $text, + $imageLinks, + $videos, + $documents, + $publishDate)); } } } public function update(array $settings, Post $post): void { - if ($post->status->value === PostStatus::PUBLISHED->value) { + if ($post->status === PostStatus::PUBLISHED) { if ($settings['vk']) { + $text = $this->generateContentForVk($post); + + $imagesFromPost = $post->images; + $imagesFromContent = $this->extractImagesFromContent($post->content); + $allImages = array_merge($imagesFromPost, $imagesFromContent); + $imageLinks = $this->generateImageLinksForVk($allImages); + $videos = $this->extractVideosFromContent($post->content); + $documents = $this->extractFilesFromContent($post->content); - $text = $this->generateContentForVk($post->content); - $images = $this->generateImageLinksForVk($post->images); $publishDate = $post->publish_at > now() ? $post->publish_at->timestamp : null; - dispatch(new UpdateVkPost($post->title, $text, $images, $post->id, $publishDate)); + dispatch(new UpdateVkPostJob( + $post->id, + $post->title, + $text, + $imageLinks, + $videos, + $documents, + $publishDate)); } } + +// +// if ($post->status === PostStatus::PUBLISHED) { +// if ($settings['vk']) { +// $text = $this->generateContentForVk($post->title, $post->content); +// $images = $this->generateImageLinksForVk($post->images); +// $publishDate = $post->publish_at > now() ? $post->publish_at->timestamp : null; +// +// dispatch(new UpdateVkPost($post->title, $text, $images, $post->id, $publishDate)); +// } +// } } - /** - * Генерирует текстовый контент для ВКонтакте. - * - * @param array $content - * @return string - */ - private function generateContentForVk(array $content): string + private function generateContentForVk($post): string + { + $message = ''; + $message .= "Название: $post->title\n\n"; + + if (is_array($post->content)) { + $message .= $this->parseContentBlocks($post->content); + } elseif (is_string($post->content)) { + $contentBlocks = json_decode($post->content, true); + if (is_array($contentBlocks)) { + $message .= $this->parseContentBlocks($contentBlocks); + } else { + $message .= strip_tags($post->content); + } + } + + if (isset($post->authors) && is_array($post->authors) && !empty($post->authors)) { + $authors = array_map(function($author) { + return json_decode('"' . $author . '"'); + }, $post->authors); + $authorLabel = (count($authors) === 1) ? "Автор:" : "Авторы:"; + $message .= "\n\n$authorLabel " . implode(', ', $authors); + } + + $message = html_entity_decode($message); + +// $maxLength = 16000; +// $reservedSpace = 200; +// $linkText = ''; + +// if ($post->slug) { +// $url = config('app.url') . "/posts/{$post->slug}"; +// $linkText = "\n\n[{$url}|Читать полностью]"; +// } +// +// $linkLength = mb_strlen($linkText); +// +// $maxMLength = $maxLength - $reservedSpace - $linkLength; +// +// if (mb_strlen($message) > $maxMLength) { +// $truncated = mb_substr($message, 0, $maxMLength); +// $lastSpace = mb_strrpos($truncated, ' '); +// if ($lastSpace !== false) { +// $truncated = mb_substr($truncated, 0, $lastSpace); +// } +// $message = $truncated . '...'; +// } +// +// $message .= $linkText; + + return $message; + } + + private function parseContentBlocks(array $blocks): string { $text = ''; - foreach ($content as $block) { - switch ($block['type']) { - case 'paragraph': - $text .= strip_tags($block['data']['content']) . "\n\n"; - break; + foreach ($blocks as $block) { + switch ($block['type'] ?? '') { case 'heading': - $text .= strip_tags($block['data']['content']) . "\n\n"; + case 'paragraph': + if (isset($block['data']['content'])) { + $text .= strip_tags($block['data']['content']) . "\n\n"; + } + break; + case 'list': + if (isset($block['data']['items']) && is_array($block['data']['items'])) { + foreach ($block['data']['items'] as $item) { + $text .= "- " . $item . "\n"; + } + $text .= "\n"; + } break; } } return trim($text); } - /** - * Генерирует ссылки на изображения для ВКонтакте. - * - * @param array $images - * @return array - */ + private function extractImagesFromContent(array $blocks): array + { + $images = []; + foreach ($blocks as $block) { + if ($block['type'] === 'image' && isset($block['data']['url']) && is_array($block['data']['url'])) { + foreach ($block['data']['url'] as $imagePath) { + $images[] = $imagePath; + } + } + } + return $images; + } + + private function extractVideosFromContent(array $blocks): array + { + $videos = []; + foreach ($blocks as $block) { + if ($block['type'] === 'video' && isset($block['data']['path'])) { + $path = Storage::url($block['data']['path']); + $path = stripslashes($path); + $videos[] = $path; + } + } + return $videos; + } + + private function extractFilesFromContent(array $blocks): array + { + $files = []; + foreach ($blocks as $block) { + if ($block['type'] === 'files' && isset($block['data']['file']) && is_array($block['data']['file'])) { + foreach ($block['data']['file'] as $file) { + if (isset($file['path'])) { + $files[] = Storage::url($file['path']); + } + } + } + } + return $files; + } + private function generateImageLinksForVk(array $images): array { return array_map(function ($file) { @@ -79,3 +210,4 @@ class VkPostPublisher }, $images); } } + diff --git a/app/Services/VK/Album/VkAlbumService.php b/app/Services/VK/Album/VkAlbumService.php index 58677db..91c86ec 100644 --- a/app/Services/VK/Album/VkAlbumService.php +++ b/app/Services/VK/Album/VkAlbumService.php @@ -36,6 +36,15 @@ class VkAlbumService ); } + public function getServerForUploadImagesOnWall($group_id) + { + return $this->vk->photos()->getWallUploadServer( + $this->vkAuthService->getToken()->access_token, + array( + 'group_id' => $group_id, + ) + ); + } public function uploadImagesToUploadServer($uploadUrl, $images) @@ -83,8 +92,6 @@ class VkAlbumService // Обработка ответа $responseData = json_decode($response, true); - - // Удаление временных файлов foreach ($localFiles as $localFile) { unlink($localFile); @@ -167,5 +174,4 @@ class VkAlbumService ]; } } - } \ No newline at end of file diff --git a/app/Services/VK/VkAuthService.php b/app/Services/VK/VkAuthService.php index d9e4e35..f4503c0 100644 --- a/app/Services/VK/VkAuthService.php +++ b/app/Services/VK/VkAuthService.php @@ -33,28 +33,51 @@ class VkAuthService } } +// 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 video docs', +// 'code_challenge' => $code_challenge, +// 'code_challenge_method' => 's256', +// ]); +// +// return redirect($url); +// } + public function redirectToProvider() { - $state = bin2hex(random_bytes(16)); // Генерация случайной строки состояния - session(['vk_state' => $state]); + $state = bin2hex(random_bytes(16)); // Генерирует случайную строку состояния (state) для защиты от CSRF-атак. + session(['vk_state' => $state]); // Сохраняет строку состояния в сессии под ключом 'vk_state'. - $code_verifier = $this->generateCodeVerifier(); - $code_challenge = $this->generateCodeChallenge($code_verifier); + $code_verifier = $this->generateCodeVerifier(); // Создает случайную строку code_verifier для протокола PKCE. + $code_challenge = $this->generateCodeChallenge($code_verifier); // Генерирует code_challenge на основе code_verifier (обычно хеш SHA-256). - session(['vk_code_verifier' => $code_verifier]); + session(['vk_code_verifier' => $code_verifier]); // Сохраняет code_verifier в сессии под ключом 'vk_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', + $url = 'https://id.vk.com/authorize?' . http_build_query([ // Формирует URL для перенаправления на страницу авторизации VK ID с параметрами: + 'response_type' => 'code', // Указывает, что нужен код авторизации. + 'client_id' => env('VK_APP_ID'), // ID приложения VK из переменных окружения. + 'redirect_uri' => env('VK_REDIRECT_URI'), // URI для перенаправления после авторизации. + 'state' => $state, // Передает строку состояния для проверки. + 'scope' => 'photos wall video docs', // Запрашивает доступ к фото, стене, видео и документам. + 'code_challenge' => $code_challenge, // Передает code_challenge для PKCE. + 'code_challenge_method' => 's256', // Указывает метод генерации code_challenge (SHA-256). ]); - return redirect($url); + return redirect($url); // Перенаправляет пользователя на сформированный URL для авторизации. } public function handleProviderCallback(Request $request) @@ -97,7 +120,7 @@ class VkAuthService 'client_id' => env('VK_APP_ID'), 'device_id' => $request->device_id, 'state' => $request->state, - 'scope' => 'photos wall', + 'scope' => 'photos wall video docs', ])->object(); } @@ -109,7 +132,7 @@ class VkAuthService 'client_id' => env('VK_APP_ID'), 'device_id' => $device_id, 'state' => $state, - 'scope' => 'photos wall', + 'scope' => 'photos wall video docs', ]); if ($response->failed()) { diff --git a/app/Services/VK/VkService.php b/app/Services/VK/VkService.php index 457b93c..9baf425 100644 --- a/app/Services/VK/VkService.php +++ b/app/Services/VK/VkService.php @@ -4,6 +4,10 @@ namespace App\Services\VK; use App\Services\VK\Album\VkAlbumService; use App\Services\VK\Wall\VkWallService; +use Exception; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Log; use VK\Client\VKApiClient; class VkService @@ -16,6 +20,7 @@ class VkService $this->wallService = new VkWallService($vk); $this->albumService = new VkAlbumService($vk); $this->public_id = env('PUBLIC_ID'); + $this->vkAuthService = new VkAuthService(); } public function getPosts(int $count = 10) @@ -28,16 +33,50 @@ class VkService return $this->wallService->getPostById($id); } - public function createPost(string $title, string $message, array $images = [], int|null $publish_date = null) +// public function createPost(string $title, string $message, array $images = [], array $videos = [], int|null $publish_date = null) +// { +// $from_group = 1; +// $attachments = []; +// +// if (!empty($images)) { +// if (count($images) <= 10) { +// $attachments[] = $this->prepareWallPhotos($images); +// } +// else{ +// $album = $this->createAlbum($title, $images); +// $attachments[] = $this->createAlbumAttachmentParam($album['id']); +// } +// } +// +// $attachmentString = implode(',', $attachments); +// +// return $this->wallService->createPost($message, $from_group, $attachmentString, $publish_date); +// } + + public function createPost(string $title, string $message, array $images = [], array $videos = [], int|null $publish_date = null) { $from_group = 1; - $album_attachment = ''; - if ($images !== []) { - $album = $this->createAlbum($title, $images); - $album_attachment = $this->createAlbumAttachmentParam($album['id']); + $attachments = []; + + if (!empty($images)) { + if (count($images) <= 10) { + $attachments[] = $this->prepareWallPhotos($images); + } else { + $attachments[] = $this->prepareWallPhotos(array_slice($images, 0, 10)); + + $album = $this->createAlbum($title, $images); + if (isset($album['id'])) { + $albumLink = "https://vk.com/album-{$this->public_id}_{$album['id']}"; + $message .= "\n[{$albumLink}|Ссылка на все фотографии]"; + } else { + Log::error('Не удалось создать альбом'); + } + } } - return $this->wallService->createPost($message, $from_group, $album_attachment, $publish_date); + $attachmentString = implode(',', $attachments); + + return $this->wallService->createPost($message, $from_group, $attachmentString, $publish_date); } public function updatePost(int $id, string $title, string $message, array $images = [], int|null $publish_date = null) @@ -59,6 +98,13 @@ class VkService return $this->wallService->updatePost($id, $message, $from_group, $album_attachment, $publish_date); } + public function deletePost(int $post_id){ + $postRelation = DB::table('posts_vk_posts')->select()->where('post_id', $post_id)->first(); + $post_id = $postRelation->vk_post_id; + $vk_post = $this->getPostById($post_id); + $this->wallService->deletePost($vk_post['id']); + } + public function createAlbum(string $title, $images) { $album = $this->albumService->createAlbum($title); @@ -89,4 +135,129 @@ class VkService return (isset($filteredAttachment[0]) ? $filteredAttachment[0] : null); } + private function prepareWallPhotos(array $images){ + $baseUrl = config('app.url'); + try { + $imagePaths = array_map(function($img) use ($baseUrl) { + return $baseUrl . $img; + }, $images); + + $photos = $this->uploadWallPhotos($imagePaths, env('PUBLIC_ID')); + + return implode(',', array_map(function($photo) { + return "photo{$photo['owner_id']}_{$photo['id']}"; + }, $photos)); + } catch (Exception $e) { + Log::error("Ошибка: " . $e->getMessage()); + } + } + + private function prepareVideos(array $videos) + { + $baseUrl = config('app.url'); + $videoPaths = array_map(function($vid) use ($baseUrl) { + return $baseUrl . $vid; + }, $videos); + return $this->uploadVideos($videoPaths, env('PUBLIC_ID')); // Возвращает массив, например: ['video123_456', 'video789_012'] + } + + private function uploadVideos(array $videoPaths, ?int $groupId = null): array + { + $uploadedVideos = []; + + foreach ($videoPaths as $videoPath) { + try { + // Шаг 1: Получение сервера для загрузки + $saveResponse = Http::get('https://api.vk.com/method/video.save', [ + 'group_id' => $groupId, + 'access_token' => $this->vkAuthService->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + if (!isset($saveData['response']['upload_url'])) { + throw new Exception('Не удалось получить сервер для загрузки видео: ' . $videoPath); + } + $uploadUrl = $saveData['response']['upload_url']; + + // Шаг 2: Загрузка видео на сервер + $uploadResponse = Http::attach( + 'video_file', + file_get_contents($videoPath), + basename($videoPath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['video_id']) || !isset($uploadData['owner_id'])) { + throw new Exception('Не удалось загрузить видео: ' . $videoPath); + } + + + $attachment = "video{$uploadData['owner_id']}_{$uploadData['video_id']}"; + $uploadedVideos[] = $attachment; + } catch (Exception $e) { + Log::error("Ошибка при загрузке видео {$videoPath}: " . $e->getMessage()); + } + } + + return $uploadedVideos; + } + + private function uploadWallPhotos(array $imagePaths, ?int $groupId = null): array + { + $uploadedPhotos = []; + + foreach ($imagePaths as $imagePath) { + try { + $uploadServerResponse = Http::get('https://api.vk.com/method/photos.getWallUploadServer', [ + 'group_id' => $groupId, + 'access_token' => $this->vkAuthService->getToken()->access_token, + 'v' => '5.131', + ]); + + $uploadServerData = $uploadServerResponse->json(); + if (!isset($uploadServerData['response']['upload_url'])) { + throw new Exception('Не удалось получить сервер для загрузки для изображения: ' . $imagePath); + } + $uploadUrl = $uploadServerData['response']['upload_url']; + + // Шаг 2: Загрузка фотографии на сервер + $uploadResponse = Http::attach( + 'photo', + file_get_contents($imagePath), + basename($imagePath) + )->post($uploadUrl); + + $uploadData = $uploadResponse->json(); + if (!isset($uploadData['photo']) || !isset($uploadData['server']) || !isset($uploadData['hash'])) { + throw new Exception('Не удалось загрузить фотографию: ' . $imagePath); + } + + // Шаг 3: Сохранение фотографии + $saveResponse = Http::get('https://api.vk.com/method/photos.saveWallPhoto', [ + 'group_id' => $groupId, + 'photo' => $uploadData['photo'], + 'server' => $uploadData['server'], + 'hash' => $uploadData['hash'], + 'access_token' => $this->vkAuthService->getToken()->access_token, + 'v' => '5.131', + ]); + + $saveData = $saveResponse->json(); + if (!isset($saveData['response'][0])) { + throw new Exception('Не удалось сохранить фотографию: ' . $imagePath); + } + + // Добавляем данные о загруженной фотографии в массив + $uploadedPhotos[] = $saveData['response'][0]; + Log::info("Фотография успешно загружена. ID: " . $saveData['response'][0]['id']); + } catch (Exception $e) { + // Логируем ошибку, но продолжаем загрузку остальных изображений + Log::error("Ошибка при загрузке изображения {$imagePath}: " . $e->getMessage()); + } + } + + return $uploadedPhotos; // Возвращаем массив с данными о загруженных фотографиях + } + } \ No newline at end of file diff --git a/app/Services/VK/Wall/VkWallService.php b/app/Services/VK/Wall/VkWallService.php index ad8f021..17bf5e5 100644 --- a/app/Services/VK/Wall/VkWallService.php +++ b/app/Services/VK/Wall/VkWallService.php @@ -12,6 +12,7 @@ use VK\Client\VKApiClient; class VkWallService { private VKApiClient $vk; + private VkAuthService $vkAuthService; private string $wallToken; private string $serviceToken; private string $publicId; @@ -24,7 +25,6 @@ class VkWallService $this->publicId = env('PUBLIC_ID'); $this->publicDomain = env('PUBLIC_DOMAIN'); $this->vkAuthService = new VkAuthService(); - } public function getPosts(int $count) @@ -38,22 +38,25 @@ class VkWallService public function getPostById(int $id) { - $post = $this->vk->wall()->getById($this->serviceToken, array( - 'posts' => '-'. $this->publicId . '_' . $id, - )); - return $post[0]; + try { + $post = $this->vk->wall()->getById($this->vkAuthService->getToken()->access_token, array( + 'posts' => '-'. $this->publicId . '_' . $id, + )); + return $post[0]; + } catch (\Exception $e) { + Log::error('Ошибка при попытке получения айди ' . $e->getMessage()); + } } - - public function createPost(string $message, int $from_group, string $attachments = '', int|null $publish_date = null) { + $token = $this->vkAuthService->getToken()->access_token; $params = [ 'owner_id' => '-' . $this->publicId, 'from_group' => $from_group, 'message' => $message, 'attachments' => $attachments, - 'access_token' => $this->wallToken, + 'access_token' => $token, 'publish_date' => $publish_date, 'v' => '5.131', ]; @@ -88,7 +91,6 @@ class VkWallService ]; } - try { return $this->vk->wall()->edit( $this->vkAuthService->getToken()->access_token, @@ -108,6 +110,36 @@ class VkWallService ]; } } + + public function deletePost(int $post_id) + { + $token = $this->vkAuthService->getToken()->access_token; + $params = [ + 'owner_id' => '-' . $this->publicId, + 'post_id' => $post_id, + 'access_token' => $token, + 'v' => '5.131', + ]; + + try { + $response = Http::asForm()->post('https://api.vk.com/method/wall.delete', $params); + + if ($response->successful() && isset($response['response']) && $response['response'] == 1) { + return [ + 'success' => true, + ]; + } else { + throw new \Exception('Ошибка API: ' . json_encode($response->json())); + } + } catch (\Exception $e) { + Log::error('Ошибка при удалении поста: ' . $e->getMessage()); + return [ + 'success' => false, + 'message' => 'Не удалось удалить пост: ' . $e->getMessage(), + ]; + } + } + public function generateAttachmentsParams(string $attachmentType, int $attachmentId) { $pubic_id = env('PUBLIC_ID'); @@ -117,6 +149,9 @@ class VkWallService } case 'doc': { + } + case 'photo': { + } } } diff --git a/composer.json b/composer.json index ae5c494..4f8e54b 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "filament/filament": "v3.2.127", "filament/spatie-laravel-settings-plugin": "^3.2", "filament/spatie-laravel-tags-plugin": "v3.2.113", + "defstudio/telegraph": "^1.59", "guava/filament-icon-picker": "2.2.4", "guzzlehttp/guzzle": "^7.8", "imangazaliev/didom": "^2.0", diff --git a/database/.DS_Store b/database/.DS_Store index 98c8f08e3275e694b3965d75d8a52764dd023454..6ca697bc3dd4c17b4aafee67f3acd4d618ce2f41 100644 GIT binary patch delta 48 zcmZn(XbG6$&nUPtU^hRb;AS3yDU6&c#mPBI`T02vlLI9rHZKr#h0}*Cs E0Dx2vhX4Qo delta 37 tcmZn(XbG6$&nU1lU^hRbz-AtSDU6#B3cB%4Y*5_HuJDUxvyvz?GXU)13#id(); + $table->string('token')->unique(); + $table->string('name')->nullable(); + + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('telegraph_bots'); + } +}; diff --git a/database/migrations/2025_04_25_195957_create_telegraph_chats_table.php b/database/migrations/2025_04_25_195957_create_telegraph_chats_table.php new file mode 100644 index 0000000..a0316d5 --- /dev/null +++ b/database/migrations/2025_04_25_195957_create_telegraph_chats_table.php @@ -0,0 +1,26 @@ +id(); + $table->string('chat_id'); + $table->string('name')->nullable(); + + $table->foreignId('telegraph_bot_id')->constrained('telegraph_bots')->cascadeOnDelete(); + $table->timestamps(); + + $table->unique(['chat_id', 'telegraph_bot_id']); + }); + } + + public function down(): void + { + Schema::dropIfExists('telegraph_chats'); + } +}; diff --git a/database/migrations/2025_04_27_203434_create_posts_tg_posts_table.php b/database/migrations/2025_04_27_203434_create_posts_tg_posts_table.php new file mode 100644 index 0000000..097c97b --- /dev/null +++ b/database/migrations/2025_04_27_203434_create_posts_tg_posts_table.php @@ -0,0 +1,30 @@ +id(); + $table->unsignedBigInteger('post_id'); // Поле post_id + $table->unsignedBigInteger('tg_post_id'); // Поле vk_post_id + $table->dateTime('unchange_time_after'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('posts_tg_posts'); + } +}; diff --git a/database/migrations/2025_05_04_155131_add_img_ids_in_posts_tg_posts_table.php b/database/migrations/2025_05_04_155131_add_img_ids_in_posts_tg_posts_table.php new file mode 100644 index 0000000..971adee --- /dev/null +++ b/database/migrations/2025_05_04_155131_add_img_ids_in_posts_tg_posts_table.php @@ -0,0 +1,28 @@ +text('media_content')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('posts_tg_posts', function (Blueprint $table) { + $table->dropColumn('media_content'); + }); + } +};