2025-06-29 21:20:45 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Modules\Lemmy\Services;
|
|
|
|
|
|
2025-06-30 18:18:30 +02:00
|
|
|
use App\Enums\PlatformEnum;
|
|
|
|
|
use App\Exceptions\PublishException;
|
2025-06-29 21:20:45 +02:00
|
|
|
use App\Models\Article;
|
|
|
|
|
use App\Models\ArticlePublication;
|
2025-07-05 01:55:53 +02:00
|
|
|
use App\Models\PlatformAccount;
|
2025-07-02 21:32:37 +02:00
|
|
|
use App\Services\Auth\LemmyAuthService;
|
2025-06-29 21:20:45 +02:00
|
|
|
use Exception;
|
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
2025-07-05 01:55:53 +02:00
|
|
|
use RuntimeException;
|
2025-06-29 21:20:45 +02:00
|
|
|
|
|
|
|
|
class LemmyPublisher
|
|
|
|
|
{
|
|
|
|
|
private LemmyApiService $api;
|
2025-07-05 01:55:53 +02:00
|
|
|
private PlatformAccount $account;
|
2025-06-29 21:20:45 +02:00
|
|
|
|
2025-07-05 01:55:53 +02:00
|
|
|
public function __construct(PlatformAccount $account)
|
2025-06-29 21:20:45 +02:00
|
|
|
{
|
2025-07-05 01:55:53 +02:00
|
|
|
$this->api = new LemmyApiService($account->instance_url);
|
|
|
|
|
$this->account = $account;
|
2025-06-29 21:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-05 01:55:53 +02:00
|
|
|
public static function fromActiveAccount(): self
|
2025-06-29 21:20:45 +02:00
|
|
|
{
|
2025-07-05 01:55:53 +02:00
|
|
|
$accounts = PlatformAccount::getActive(PlatformEnum::LEMMY);
|
|
|
|
|
|
|
|
|
|
if ($accounts->isEmpty()) {
|
|
|
|
|
throw new RuntimeException('No active Lemmy accounts configured');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new self($accounts->first());
|
2025-06-29 21:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-06-30 18:18:30 +02:00
|
|
|
/**
|
|
|
|
|
* @throws PublishException
|
|
|
|
|
*/
|
2025-06-29 21:20:45 +02:00
|
|
|
public function publish(Article $article, array $extractedData): ArticlePublication
|
|
|
|
|
{
|
2025-06-30 18:18:30 +02:00
|
|
|
try {
|
2025-07-05 01:55:53 +02:00
|
|
|
$token = LemmyAuthService::getToken($this->account);
|
2025-06-30 18:18:30 +02:00
|
|
|
$communityId = $this->getCommunityId();
|
2025-06-29 21:20:45 +02:00
|
|
|
|
2025-07-02 20:26:28 +02:00
|
|
|
$languageId = $this->getLanguageIdForSource($article->url);
|
|
|
|
|
|
2025-06-30 18:18:30 +02:00
|
|
|
$postData = $this->api->createPost(
|
|
|
|
|
$token,
|
|
|
|
|
$extractedData['title'] ?? 'Untitled',
|
|
|
|
|
$extractedData['description'] ?? '',
|
|
|
|
|
$communityId,
|
|
|
|
|
$article->url,
|
2025-07-02 20:26:28 +02:00
|
|
|
$extractedData['thumbnail'] ?? null,
|
|
|
|
|
$languageId
|
2025-06-30 18:18:30 +02:00
|
|
|
);
|
2025-06-29 21:20:45 +02:00
|
|
|
|
2025-06-30 18:18:30 +02:00
|
|
|
return $this->createPublicationRecord($article, $postData, $communityId);
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
throw new PublishException($article, PlatformEnum::LEMMY, $e);
|
|
|
|
|
}
|
2025-06-29 21:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-05 01:55:53 +02:00
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2025-06-29 21:20:45 +02:00
|
|
|
private function getCommunityId(): int
|
|
|
|
|
{
|
2025-07-05 01:55:53 +02:00
|
|
|
$community = config('lemmy.community');
|
|
|
|
|
|
|
|
|
|
if (! $community) {
|
|
|
|
|
throw new RuntimeException('No community configured in config');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$cacheKey = "lemmy_community_id_$community";
|
2025-06-30 21:28:15 +02:00
|
|
|
$cachedId = Cache::get($cacheKey);
|
2025-07-02 20:26:28 +02:00
|
|
|
|
2025-06-30 21:28:15 +02:00
|
|
|
if ($cachedId) {
|
|
|
|
|
return $cachedId;
|
|
|
|
|
}
|
2025-07-02 20:26:28 +02:00
|
|
|
|
2025-07-05 01:55:53 +02:00
|
|
|
$communityId = $this->api->getCommunityId($community);
|
2025-06-30 21:28:15 +02:00
|
|
|
Cache::put($cacheKey, $communityId, 3600);
|
2025-07-02 20:26:28 +02:00
|
|
|
|
2025-06-30 21:28:15 +02:00
|
|
|
return $communityId;
|
2025-06-29 21:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function createPublicationRecord(Article $article, array $postData, int $communityId): ArticlePublication
|
|
|
|
|
{
|
|
|
|
|
return ArticlePublication::create([
|
|
|
|
|
'article_id' => $article->id,
|
|
|
|
|
'post_id' => $postData['post_view']['post']['id'],
|
|
|
|
|
'community_id' => $communityId,
|
2025-07-05 01:55:53 +02:00
|
|
|
'published_by' => $this->account->username,
|
2025-06-29 21:20:45 +02:00
|
|
|
'published_at' => now(),
|
|
|
|
|
'platform' => 'lemmy',
|
|
|
|
|
'publication_data' => $postData,
|
|
|
|
|
]);
|
|
|
|
|
}
|
2025-07-02 20:26:28 +02:00
|
|
|
|
|
|
|
|
private function getLanguageIdForSource(string $url): ?int
|
|
|
|
|
{
|
|
|
|
|
// TODO this will be obsolete when sources can be created from the UI, so we can remove these hard-coded sources
|
|
|
|
|
|
|
|
|
|
// VRT articles are in Dutch
|
|
|
|
|
if (str_contains($url, 'vrt.be')) {
|
|
|
|
|
return $this->getLanguageId('nl'); // Dutch
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Belga articles are in English (based on URL structure)
|
|
|
|
|
if (str_contains($url, 'belganewsagency.eu')) {
|
|
|
|
|
return $this->getLanguageId('en'); // English
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null; // Default to no language specified
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getLanguageId(string $languageCode): ?int
|
|
|
|
|
{
|
2025-07-05 01:55:53 +02:00
|
|
|
$cacheKey = "lemmy_language_id_$languageCode";
|
2025-07-02 20:26:28 +02:00
|
|
|
$cachedId = Cache::get($cacheKey);
|
|
|
|
|
|
|
|
|
|
if ($cachedId !== null) {
|
|
|
|
|
return $cachedId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$languages = $this->api->getLanguages();
|
|
|
|
|
|
|
|
|
|
foreach ($languages as $language) {
|
|
|
|
|
if (isset($language['code']) && $language['code'] === $languageCode) {
|
|
|
|
|
$languageId = $language['id'];
|
|
|
|
|
Cache::put($cacheKey, $languageId, 3600);
|
|
|
|
|
return $languageId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cache null result to avoid repeated API calls
|
|
|
|
|
Cache::put($cacheKey, null, 3600);
|
|
|
|
|
return null;
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
logger()->warning('Failed to get language ID', [
|
|
|
|
|
'language_code' => $languageCode,
|
|
|
|
|
'error' => $e->getMessage()
|
|
|
|
|
]);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-30 18:18:30 +02:00
|
|
|
}
|