Automatic AI Posts, setup automatizado e seeder de permalinks/avatar
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
This commit is contained in:
1 parent
5891467266
commit
0ea3651fb5
73 files changed
+8858
-39
No files matched your search
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace AAP\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* PSR-4 style autoloader: AAP\Foo\Bar -> src/Foo/Bar.php
|
||||
*/
|
||||
final class Autoloader {
|
||||
|
||||
private static string $base = '';
|
||||
|
||||
public static function register(string $base): void {
|
||||
self::$base = rtrim($base, '/') . '/';
|
||||
spl_autoload_register([self::class, 'load']);
|
||||
}
|
||||
|
||||
public static function load(string $class): void {
|
||||
if (strncmp($class, 'AAP\\', 4) !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$relative = substr($class, 4);
|
||||
$path = self::$base . str_replace('\\', '/', $relative) . '.php';
|
||||
|
||||
if (is_readable($path)) {
|
||||
require_once $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace AAP\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* A module is a self-contained unit of the plugin.
|
||||
*
|
||||
* Lifecycle:
|
||||
* register() — attach hooks that must exist before boot (rarely needed).
|
||||
* boot() — attach runtime hooks; dependencies are already booted here.
|
||||
*/
|
||||
abstract class Module {
|
||||
|
||||
/** Unique module id (used by dependencies()). */
|
||||
abstract public function id(): string;
|
||||
|
||||
/** @return string[] ids of modules that must boot first. */
|
||||
public function dependencies(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** Called before any boot(), in dependency order. */
|
||||
public function register(): void {
|
||||
}
|
||||
|
||||
/** Called after every module was registered, in dependency order. */
|
||||
public function boot(): void {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
namespace AAP\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Central place for every option the plugin stores.
|
||||
*
|
||||
* IMPORTANT: existing option names are kept exactly as they were in 1.x so
|
||||
* upgrading never loses configuration.
|
||||
*/
|
||||
final class Options {
|
||||
|
||||
public const DEFAULTS = [
|
||||
// Feeds
|
||||
'aap_feeds' => "https://feeds.bbci.co.uk/news/rss.xml\nhttps://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml",
|
||||
'aap_feed_max_items' => 10,
|
||||
|
||||
// GNews
|
||||
'aap_gnews_api_key' => '',
|
||||
'aap_gnews_categories' => [],
|
||||
'aap_gnews_max' => 10,
|
||||
|
||||
// Generation
|
||||
'aap_min_posts' => 1,
|
||||
'aap_max_posts' => 3,
|
||||
'aap_post_status' => 'draft',
|
||||
'aap_post_language' => 'pt-BR',
|
||||
|
||||
// Editorial instructions
|
||||
'aap_relevance_description' => '',
|
||||
'aap_negative_profile' => '',
|
||||
'aap_rewrite_instructions' => '',
|
||||
|
||||
// AI provider
|
||||
'aap_ai_provider' => 'nvidia',
|
||||
'aap_nvidia_api_key' => '',
|
||||
'aap_openai_api_key' => '',
|
||||
'aap_anthropic_api_key' => '',
|
||||
'aap_openrouter_api_key' => '',
|
||||
'aap_custom_api_key' => '',
|
||||
'aap_custom_base_url' => '',
|
||||
'aap_vertex_project' => '',
|
||||
'aap_vertex_location' => 'us-central1',
|
||||
'aap_vertex_service_account'=> '',
|
||||
'aap_model' => '',
|
||||
'aap_ai_timeout' => 120,
|
||||
'aap_ai_max_retries' => 3,
|
||||
|
||||
// Images
|
||||
'aap_image_instructions' => '',
|
||||
'aap_image_caption' => '',
|
||||
|
||||
// Scheduling
|
||||
'aap_auto_interval_value' => 24,
|
||||
'aap_auto_interval_unit' => 'hours',
|
||||
'aap_auto_enabled' => false,
|
||||
'aap_auto_user_id' => 0,
|
||||
'aap_next_auto_run' => 0,
|
||||
'aap_last_run' => '',
|
||||
|
||||
// App
|
||||
'aap_app_language' => 'pt_BR',
|
||||
];
|
||||
|
||||
public static function get(string $key, $fallback = null) {
|
||||
$default = self::DEFAULTS[$key] ?? $fallback;
|
||||
$value = get_option($key, $default);
|
||||
|
||||
return $value === null ? $default : $value;
|
||||
}
|
||||
|
||||
public static function set(string $key, $value): bool {
|
||||
return update_option($key, $value);
|
||||
}
|
||||
|
||||
public static function bool(string $key): bool {
|
||||
return (bool) self::get($key);
|
||||
}
|
||||
|
||||
public static function int(string $key): int {
|
||||
return (int) self::get($key);
|
||||
}
|
||||
|
||||
public static function string(string $key): string {
|
||||
return (string) self::get($key);
|
||||
}
|
||||
|
||||
/** Creates missing options without touching existing ones. */
|
||||
public static function install_defaults(): void {
|
||||
foreach (self::DEFAULTS as $key => $value) {
|
||||
if (get_option($key, null) === null) {
|
||||
add_option($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace AAP\Core;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Resolves the module dependency tree and boots it in order.
|
||||
*/
|
||||
final class Registry {
|
||||
|
||||
/** @var array<string, Module> */
|
||||
private static array $modules = [];
|
||||
|
||||
private static bool $booted = false;
|
||||
|
||||
public static function add(Module $module): void {
|
||||
self::$modules[$module->id()] = $module;
|
||||
}
|
||||
|
||||
public static function get(string $id): ?Module {
|
||||
return self::$modules[$id] ?? null;
|
||||
}
|
||||
|
||||
/** @return array<string, Module> */
|
||||
public static function all(): array {
|
||||
return self::$modules;
|
||||
}
|
||||
|
||||
public static function boot(): void {
|
||||
if (self::$booted) {
|
||||
return;
|
||||
}
|
||||
self::$booted = true;
|
||||
|
||||
$order = self::resolve();
|
||||
|
||||
foreach ($order as $id) {
|
||||
self::$modules[$id]->register();
|
||||
}
|
||||
|
||||
foreach ($order as $id) {
|
||||
self::$modules[$id]->boot();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Topological sort. Missing dependencies are skipped with a notice
|
||||
* (a broken module must not take the whole admin down).
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private static function resolve(): array {
|
||||
$resolved = [];
|
||||
$visiting = [];
|
||||
|
||||
$visit = function (string $id) use (&$visit, &$resolved, &$visiting): void {
|
||||
if (isset($resolved[$id]) || isset($visiting[$id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$module = self::$modules[$id] ?? null;
|
||||
if (!$module) {
|
||||
return;
|
||||
}
|
||||
|
||||
$visiting[$id] = true;
|
||||
|
||||
foreach ($module->dependencies() as $dependency) {
|
||||
if (!isset(self::$modules[$dependency])) {
|
||||
if (function_exists('error_log')) {
|
||||
error_log("[Automatic AI Posts] Module \"{$id}\" requires missing module \"{$dependency}\".");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$visit($dependency);
|
||||
}
|
||||
|
||||
unset($visiting[$id]);
|
||||
$resolved[$id] = true;
|
||||
};
|
||||
|
||||
foreach (array_keys(self::$modules) as $id) {
|
||||
$visit($id);
|
||||
}
|
||||
|
||||
return array_keys($resolved);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user