Plugin automatic-ai-posts (v2.0.0) - Pipeline completo: coleta de feeds RSS/Atom, filtro por IA, reescrita, categoria, imagem de capa e agendamento - Página de configuração com teste real do provedor de IA - Editor: botão para refazer imagem de capa e metadados, textos em PT-BR - Ajuda com botão Pix (copia-e-cola + QR) e sem nota de avaliação - Cache-busting por mtime nos assets do admin Tema gustavoo-portfolio - Marca do cabeçalho reduzida a "Gustavo" - Suporte a título próprio da marca Setup (docker/wp-init.sh, compose.yaml) - Plugins de terceiros (Post Views Counter, Rank Math, Site Kit) instalados do WordPress.org em vez de versionados - Falha explícita ao final se algum plugin obrigatório não instalar - WPCLI_UID/WPCLI_GID configuráveis Seeder do tema - Links permanentes "Dia e nome" e avatar "Iniciais" aplicados apenas em instalação nova, com regravação do .htaccess
112 lines
3.1 KiB
PHP
112 lines
3.1 KiB
PHP
<?php
|
|
namespace AAP\Modules\Images;
|
|
|
|
use AAP\Modules\Logs\Logger;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* DuckDuckGo image search (no API key required).
|
|
*
|
|
* The query itself is produced by the AI (see AiService::image_plan), so the
|
|
* blog owner's image instructions influence what is searched.
|
|
*/
|
|
final class DuckDuckGo {
|
|
|
|
private int $timeout;
|
|
|
|
public function __construct(int $timeout = 30) {
|
|
$this->timeout = max(10, $timeout);
|
|
}
|
|
|
|
/**
|
|
* @return string[] image URLs (best first)
|
|
*/
|
|
public function search(string $query, int $limit = 10): array {
|
|
$query = trim($query);
|
|
if ($query === '') {
|
|
return [];
|
|
}
|
|
|
|
$encoded = urlencode($query);
|
|
$token = $this->vqd($encoded);
|
|
|
|
if ($token === '') {
|
|
Logger::warn('DuckDuckGo: vqd token not found for "' . $query . '".', 'images');
|
|
return [];
|
|
}
|
|
|
|
return $this->fetch($encoded, $token, $limit);
|
|
}
|
|
|
|
private function vqd(string $encoded_query): string {
|
|
$response = wp_remote_get(
|
|
'https://duckduckgo.com/?q=' . $encoded_query . '&iax=images&ia=images',
|
|
[
|
|
'timeout' => $this->timeout,
|
|
'user-agent' => 'Mozilla/5.0 (compatible; automatic-ai-posts/2.0)',
|
|
]
|
|
);
|
|
|
|
if (is_wp_error($response)) {
|
|
Logger::warn('DuckDuckGo: token request failed: ' . $response->get_error_message(), 'images');
|
|
return '';
|
|
}
|
|
|
|
$body = (string) wp_remote_retrieve_body($response);
|
|
|
|
if (preg_match('/vqd=([\d-]+)/', $body, $m)) {
|
|
return $m[1];
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return string[]
|
|
*/
|
|
private function fetch(string $encoded_query, string $token, int $limit): array {
|
|
$url = add_query_arg([
|
|
'l' => 'us-en',
|
|
'o' => 'json',
|
|
'q' => $encoded_query,
|
|
'vqd' => $token,
|
|
'f' => ',,,,,',
|
|
'p' => '1',
|
|
], 'https://duckduckgo.com/i.js');
|
|
|
|
$response = wp_remote_get($url, [
|
|
'timeout' => $this->timeout,
|
|
'headers' => [
|
|
'Referer' => 'https://duckduckgo.com/',
|
|
'User-Agent' => 'Mozilla/5.0 (compatible; automatic-ai-posts/2.0)',
|
|
],
|
|
]);
|
|
|
|
if (is_wp_error($response)) {
|
|
Logger::warn('DuckDuckGo: image request failed: ' . $response->get_error_message(), 'images');
|
|
return [];
|
|
}
|
|
|
|
$data = json_decode((string) wp_remote_retrieve_body($response), true);
|
|
|
|
if (!isset($data['results']) || !is_array($data['results'])) {
|
|
Logger::warn('DuckDuckGo: response without results.', 'images');
|
|
return [];
|
|
}
|
|
|
|
$urls = [];
|
|
foreach ($data['results'] as $result) {
|
|
$image = (string) ($result['image'] ?? '');
|
|
if ($image !== '') {
|
|
$urls[] = $image;
|
|
if (count($urls) >= $limit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $urls;
|
|
}
|
|
}
|