fedi-feed-router/app/Modules/Lemmy/Services/LemmyApiService.php

214 lines
7 KiB
PHP
Raw Permalink Normal View History

2025-06-29 21:20:45 +02:00
<?php
namespace App\Modules\Lemmy\Services;
2025-06-30 19:54:43 +02:00
use App\Enums\PlatformEnum;
use App\Models\PlatformChannelPost;
2025-06-29 21:20:45 +02:00
use App\Modules\Lemmy\LemmyRequest;
use Exception;
class LemmyApiService
{
private string $instance;
public function __construct(string $instance)
{
$this->instance = $instance;
}
public function login(string $username, string $password): ?string
{
2025-08-09 13:48:25 +02:00
// Try HTTPS first; on failure, optionally retry with HTTP to support dev instances
$schemesToTry = [];
if (preg_match('/^https?:\/\//i', $this->instance)) {
// Preserve user-provided scheme as first try
$schemesToTry[] = strtolower(str_starts_with($this->instance, 'http://') ? 'http' : 'https');
} else {
// Default order: https then http
$schemesToTry = ['https', 'http'];
}
foreach ($schemesToTry as $idx => $scheme) {
try {
$request = new LemmyRequest($this->instance);
// ensure scheme used matches current attempt
$request = $request->withScheme($scheme);
$response = $request->post('user/login', [
'username_or_email' => $username,
'password' => $password,
2025-06-29 21:20:45 +02:00
]);
2025-08-09 13:48:25 +02:00
if (!$response->successful()) {
$responseBody = $response->body();
logger()->error('Lemmy login failed', [
'status' => $response->status(),
'body' => $responseBody,
'scheme' => $scheme,
]);
// Check if it's a rate limit error
if (str_contains($responseBody, 'rate_limit_error')) {
throw new Exception('Rate limited by Lemmy instance. Please wait a moment and try again.');
}
// If first attempt failed and there is another scheme to try, continue loop
if ($idx === 0 && count($schemesToTry) > 1) {
continue;
}
return null;
}
$data = $response->json();
return $data['jwt'] ?? null;
} catch (Exception $e) {
2026-02-25 20:22:02 +01:00
// Re-throw rate limit exceptions immediately
if (str_contains($e->getMessage(), 'Rate limited')) {
throw $e;
}
2025-08-09 13:48:25 +02:00
logger()->error('Lemmy login exception', ['error' => $e->getMessage(), 'scheme' => $scheme]);
// If this was the first attempt and HTTPS, try HTTP next
if ($idx === 0 && in_array('http', $schemesToTry, true)) {
continue;
}
2025-06-29 21:20:45 +02:00
return null;
}
}
2025-08-09 13:48:25 +02:00
return null;
2025-06-29 21:20:45 +02:00
}
2025-07-06 10:53:37 +02:00
public function getCommunityId(string $communityName, string $token): int
2025-06-29 21:20:45 +02:00
{
try {
2025-07-02 21:32:37 +02:00
$request = new LemmyRequest($this->instance, $token);
2025-06-29 21:20:45 +02:00
$response = $request->get('community', ['name' => $communityName]);
if (!$response->successful()) {
throw new Exception('Failed to fetch community: ' . $response->status());
}
$data = $response->json();
return $data['community_view']['community']['id'] ?? throw new Exception('Community not found');
} catch (Exception $e) {
logger()->error('Community lookup failed', ['error' => $e->getMessage()]);
throw $e;
2025-06-30 19:54:43 +02:00
}
}
2025-07-06 10:53:37 +02:00
public function syncChannelPosts(string $token, int $platformChannelId, string $communityName): void
2025-06-30 19:54:43 +02:00
{
try {
$request = new LemmyRequest($this->instance, $token);
$response = $request->get('post/list', [
2025-07-06 10:53:37 +02:00
'community_id' => $platformChannelId,
2025-06-30 19:54:43 +02:00
'limit' => 50,
'sort' => 'New'
]);
if (!$response->successful()) {
logger()->warning('Failed to sync channel posts', [
'status' => $response->status(),
2025-07-06 10:53:37 +02:00
'platform_channel_id' => $platformChannelId
2025-06-30 19:54:43 +02:00
]);
return;
}
$data = $response->json();
$posts = $data['posts'] ?? [];
foreach ($posts as $postData) {
$post = $postData['post'];
2025-07-02 21:32:37 +02:00
2025-06-30 19:54:43 +02:00
PlatformChannelPost::storePost(
PlatformEnum::LEMMY,
2025-07-06 10:53:37 +02:00
(string) $platformChannelId,
2025-06-30 19:54:43 +02:00
$communityName,
(string) $post['id'],
$post['url'] ?? null,
$post['name'] ?? null,
isset($post['published']) ? new \DateTime($post['published']) : null
);
}
logger()->info('Synced channel posts', [
2025-07-06 10:53:37 +02:00
'platform_channel_id' => $platformChannelId,
2025-06-30 19:54:43 +02:00
'posts_count' => count($posts)
]);
} catch (Exception $e) {
logger()->error('Exception while syncing channel posts', [
'error' => $e->getMessage(),
2025-07-06 10:53:37 +02:00
'platform_channel_id' => $platformChannelId
2025-06-30 19:54:43 +02:00
]);
2025-06-29 21:20:45 +02:00
}
}
2025-07-07 00:51:32 +02:00
/**
* @return array<string, mixed>
*/
2025-07-06 10:53:37 +02:00
public function createPost(string $token, string $title, string $body, int $platformChannelId, ?string $url = null, ?string $thumbnail = null, ?int $languageId = null): array
2025-06-29 21:20:45 +02:00
{
try {
$request = new LemmyRequest($this->instance, $token);
2025-06-30 18:18:30 +02:00
$postData = [
2025-06-29 21:20:45 +02:00
'name' => $title,
'body' => $body,
2025-07-06 10:53:37 +02:00
'community_id' => $platformChannelId,
2025-06-30 18:18:30 +02:00
];
if ($url) {
$postData['url'] = $url;
}
if ($thumbnail) {
$postData['custom_thumbnail'] = $thumbnail;
}
2025-07-02 20:26:28 +02:00
if ($languageId) {
$postData['language_id'] = $languageId;
}
2025-06-30 18:18:30 +02:00
$response = $request->post('post', $postData);
2025-06-29 21:20:45 +02:00
if (!$response->successful()) {
throw new Exception('Failed to create post: ' . $response->status() . ' - ' . $response->body());
}
return $response->json();
} catch (Exception $e) {
logger()->error('Post creation failed', ['error' => $e->getMessage()]);
throw $e;
}
}
2025-07-02 20:26:28 +02:00
2025-07-07 00:51:32 +02:00
/**
* @return array<int, mixed>
*/
2025-07-02 20:26:28 +02:00
public function getLanguages(): array
{
try {
$request = new LemmyRequest($this->instance);
$response = $request->get('site');
if (!$response->successful()) {
logger()->warning('Failed to fetch site languages', [
'status' => $response->status()
]);
return [];
}
$data = $response->json();
return $data['all_languages'] ?? [];
} catch (Exception $e) {
logger()->error('Exception while fetching languages', [
'error' => $e->getMessage()
]);
return [];
}
}
2025-06-30 18:18:30 +02:00
}