You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
5.6 KiB
144 lines
5.6 KiB
<?php |
|
|
|
declare(strict_types=1); |
|
|
|
namespace App\Command; |
|
|
|
use App\Enum\IndexStatusEnum; |
|
use App\Factory\ArticleFactory; |
|
use App\Service\NostrClient; |
|
use Doctrine\ORM\EntityManagerInterface; |
|
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface; |
|
use swentel\nostr\Event\Event; |
|
use swentel\nostr\Sign\Sign; |
|
use Symfony\Component\Console\Attribute\AsCommand; |
|
use Symfony\Component\Console\Command\Command; |
|
use Symfony\Component\Console\Input\InputArgument; |
|
use Symfony\Component\Console\Input\InputInterface; |
|
use Symfony\Component\Console\Output\OutputInterface; |
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; |
|
use Symfony\Component\Finder\Finder; |
|
use Symfony\Component\Yaml\Yaml; |
|
use Symfony\Contracts\Cache\CacheInterface; |
|
use Redis as RedisClient; |
|
|
|
#[AsCommand(name: 'app:yaml_to_nostr', description: 'Traverses folders, converts YAML files to JSON using object mapping, and saves the result in Redis cache.')] |
|
class NostrEventFromYamlDefinitionCommand extends Command |
|
{ |
|
private string $nsec; |
|
|
|
public function __construct(private readonly CacheInterface $redisCache, |
|
private readonly NostrClient $client, |
|
private readonly ArticleFactory $factory, |
|
ParameterBagInterface $bag, |
|
private readonly ObjectPersisterInterface $itemPersister, |
|
private readonly EntityManagerInterface $entityManager, |
|
private readonly RedisClient $redis) |
|
{ |
|
$this->nsec = $bag->get('nsec'); |
|
parent::__construct(); |
|
} |
|
|
|
protected function configure(): void |
|
{ |
|
$this |
|
->addArgument('folder', InputArgument::REQUIRED, 'The folder location to start scanning from.'); |
|
} |
|
|
|
protected function execute(InputInterface $input, OutputInterface $output): int |
|
{ |
|
$folder = $input->getArgument('folder'); |
|
|
|
// Use Symfony Finder to locate YAML files recursively |
|
$finder = new Finder(); |
|
$finder->files() |
|
->in($folder) |
|
->name('*.yaml') |
|
->name('*.yml'); |
|
|
|
if (!$finder->hasResults()) { |
|
$output->writeln('<comment>No YAML files found in the specified directory.</comment>'); |
|
return Command::SUCCESS; |
|
} |
|
|
|
$articleSlugsList = []; |
|
|
|
foreach ($finder as $file) { |
|
$filePath = $file->getRealPath(); |
|
$output->writeln("<info>Processing file: $filePath</info>"); |
|
$yamlContent = Yaml::parseFile($filePath); // This parses the YAML file |
|
|
|
try { |
|
// Deserialize YAML content into an Event object |
|
$event = new Event(); |
|
$event->setKind(30040); |
|
$tags = $yamlContent['tags']; |
|
$event->setTags($tags); |
|
$items = array_filter($tags, function ($tag) { |
|
return ($tag[0] === 'a'); |
|
}); |
|
foreach ($items as $one) { |
|
$parts = explode(':', $one[1]); |
|
$articleSlugsList[] = end($parts); |
|
} |
|
|
|
$signer = new Sign(); |
|
$signer->signEvent($event, $this->nsec); |
|
|
|
// Save to cache |
|
$slug = array_filter($tags, function ($tag) { |
|
return ($tag[0] === 'd'); |
|
}); |
|
$slug = $slug[0][1] ?? null; |
|
if ($slug) { |
|
$cacheKey = 'magazine-' . $slug; |
|
$cacheItem = $this->redisCache->getItem($cacheKey); |
|
$cacheItem->set($event); |
|
$this->redisCache->save($cacheItem); |
|
|
|
// If top-level magazine (has 'a' tags referencing 30040 categories), record slug |
|
$isTopLevelMagazine = false; |
|
foreach ($tags as $t) { |
|
if (($t[0] ?? null) === 'a' && isset($t[1]) && str_starts_with((string)$t[1], '30040:')) { |
|
$isTopLevelMagazine = true; break; |
|
} |
|
} |
|
if ($isTopLevelMagazine) { |
|
try { $this->redis->sAdd('magazine_slugs', $slug); } catch (\Throwable) {} |
|
} |
|
} |
|
|
|
$output->writeln("<info>Saved index.</info>"); |
|
} catch (\Exception $e) { |
|
$output->writeln("<error>Error deserializing YAML in file: $filePath. Message: {$e->getMessage()}</error>"); |
|
continue; |
|
} |
|
} |
|
|
|
// crawl relays for all the articles and save to db |
|
$fresh = $this->client->getArticles($articleSlugsList); |
|
$articles = []; |
|
foreach ($fresh as $item) { |
|
$article = $this->factory->createFromLongFormContentEvent($item); |
|
$article->setIndexStatus(IndexStatusEnum::TO_BE_INDEXED); |
|
$this->entityManager->persist($article); |
|
$articles[] = $article; |
|
} |
|
$this->entityManager->flush(); |
|
|
|
// to elastic |
|
if (count($articles) > 0 ) { |
|
$this->itemPersister->insertMany($articles); // Insert or skip existing |
|
// Set all articles as indexed |
|
foreach ($articles as $article) { |
|
$article->setIndexStatus(IndexStatusEnum::INDEXED); |
|
$this->entityManager->persist($article); |
|
} |
|
$this->entityManager->flush(); |
|
$output->writeln('<info>Added to index.</info>'); |
|
} |
|
|
|
$output->writeln('<info>Conversion complete.</info>'); |
|
return Command::SUCCESS; |
|
} |
|
}
|
|
|