0% encontró este documento útil (0 votos)
4 vistas30 páginas

WordpressAutoPost Code

El documento describe una interfaz gráfica de usuario (GUI) en Python para un sistema de publicación automática en WordPress, que permite a los usuarios configurar y gestionar la publicación de archivos desde Google Drive. Incluye funcionalidades como la selección de marca, categorías y etiquetas, así como modos de publicación inmediata, programada o en cola. Además, se implementa un sistema de registro para manejar errores y el estado del proceso de publicación.

Cargado por

Carlos Lopez Gil
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
4 vistas30 páginas

WordpressAutoPost Code

El documento describe una interfaz gráfica de usuario (GUI) en Python para un sistema de publicación automática en WordPress, que permite a los usuarios configurar y gestionar la publicación de archivos desde Google Drive. Incluye funcionalidades como la selección de marca, categorías y etiquetas, así como modos de publicación inmediata, programada o en cola. Además, se implementa un sistema de registro para manejar errores y el estado del proceso de publicación.

Cargado por

Carlos Lopez Gil
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

gui.

py:
import tkinter as tk
from tkinter import filedialog, messagebox
from tkinter import ttk
import threading
import os
import json
import sys

print("Iniciando GUI...") # Verifica que el script se está ejecutando

try:
​ from main import process_single_file, batch_mode, schedule_mode, queue_mode,
init_db, save_config, load_config, list_files_in_drive
except Exception as e:
​ print(f"Error importando módulos de [Link]: {e}")
​ [Link]("Error", f"Error importando módulos de [Link]: {e}")
​ [Link](1)

class AutoPostGUI([Link]):
​ def __init__(self):
​ super().__init__()
​ [Link]("WordPress AutoPost")
​ [Link]("520x620")
​ [Link](False, False)
​ self.stop_event = [Link]()
​ self.create_widgets()
​ try:
​ init_db()
​ except Exception as e:
​ print(f"Error inicializando la base de datos: {e}")
​ [Link]("Error", f"Error inicializando la base de datos: {str(e)}")

​ def create_widgets(self):
​ [Link](self, text="ID de carpeta de Google Drive:").pack(pady=(10, 0))
​ self.folder_entry = [Link](self, width=60)
​ self.folder_entry.pack()

​ [Link](self, text="Marca:").pack(pady=(10, 0))


​ self.brand_var = [Link](value="Asus")
​ brands = ["Asus", "Dell", "HP", "Lenovo", "Apple", "Otra"]
​ self.brand_menu = [Link](self, self.brand_var, *brands)
​ self.brand_menu.pack()

​ [Link](self, text="Categorías (IDs, separadas por coma):").pack(pady=(10, 0))


​ self.cat_entry = [Link](self, width=60)
​ self.cat_entry.pack()

​ [Link](self, text="Etiquetas (IDs, separadas por coma):").pack(pady=(10, 0))


​ self.tag_entry = [Link](self, width=60)
​ self.tag_entry.pack()

​ [Link](self, text="Modo de publicación:").pack(pady=(10, 0))


​ self.mode_var = [Link](value="batch")
​ modes = [("Inmediata", "batch"), ("Programada", "schedule"), ("Cola", "queue")]
​ for text, mode in modes:
​ [Link](self, text=text, variable=self.mode_var, value=mode).pack(anchor=tk.W)

​ btn_frame = [Link](self)
​ btn_frame.pack(pady=20)
​ self.start_btn = [Link](btn_frame, text="Iniciar", command=self.start_process)
​ self.start_btn.pack(side=[Link], padx=5)
​ self.stop_btn = [Link](btn_frame, text="Detener", command=self.stop_process,
state=[Link])
​ self.stop_btn.pack(side=[Link], padx=5)

​ self.progress_var = [Link]()
​ self.progress_bar = [Link](self, variable=self.progress_var, maximum=100,
length=400)
​ self.progress_bar.pack(pady=(10, 0))

​ self.status_text = [Link](self, height=10, width=60, state=[Link])


​ self.status_text.pack(pady=(10, 0))

​ def start_process(self):
​ folder_id = self.folder_entry.get().strip()
​ brand = self.brand_var.get()
​ categories = self.cat_entry.get().strip()
​ tags = self.tag_entry.get().strip()
​ mode = self.mode_var.get()

​ if not all([folder_id, brand, categories, tags]):


​ print("Todos los campos son obligatorios.")
​ [Link]("Error", "Todos los campos son obligatorios.")
​ return
​ try:
​ categories = [int([Link]()) for x in [Link](",") if [Link]().isdigit()]
​ tags = [int([Link]()) for x in [Link](",") if [Link]().isdigit()]
​ except Exception as e:
​ print(f"Las categorías y etiquetas deben ser números separados por coma. {e}")
​ [Link]("Error", "Las categorías y etiquetas deben ser números
separados por coma.")
​ return

​ config = {
​ 'folder_id': folder_id,
​ 'brand': brand,
​ 'categories': categories,
​ 'tags': tags
​ }
​ try:
​ save_config('last_config', config)
​ except Exception as e:
​ print(f"Error guardando configuración: {e}")
​ [Link]("Error", f"Error guardando configuración: {e}")
​ return

​ self.status_text.config(state=[Link])
​ self.status_text.delete(1.0, [Link])
​ self.status_text.insert([Link], f"Iniciando proceso en modo '{mode}'...\n")
​ self.status_text.config(state=[Link])
​ self.progress_var.set(0)
​ self.stop_event.clear()
​ self.start_btn.config(state=[Link])
​ self.stop_btn.config(state=[Link])

​ [Link](target=self.run_mode, args=(mode, config), daemon=True).start()

​ def stop_process(self):
​ self.stop_event.set()
​ self.append_status("Procesamiento detenido por el usuario.\n")
​ self.start_btn.config(state=[Link])
​ self.stop_btn.config(state=[Link])

​ def run_mode(self, mode, config):


​ try:
​ folder_id = config['folder_id']
​ files = []
​ if mode in ["batch", "schedule", "queue"]:
​ try:
​ files = list_files_in_drive(folder_id)
​ except Exception as e:
​ self.append_status(f"Error obteniendo archivos de Google Drive: {e}\n")
​ self.start_btn.config(state=[Link])
​ self.stop_btn.config(state=[Link])
​ return

​ total = len(files)
​ if total == 0 and mode != "queue":
​ self.append_status("No se encontraron archivos en la carpeta.\n")
​ self.start_btn.config(state=[Link])
​ self.stop_btn.config(state=[Link])
​ return

​ processed = 0

​ if mode == "batch":
​ self.append_status("Publicando todos los archivos ahora...\n")
​ for i, file in enumerate(files, 1):
​ if self.stop_event.is_set():
​ break
​ result = process_single_file(file, config)
​ processed += 1 if result else 0
​ self.update_progress(i, total)
​ self.append_status(f"Progreso: {i}/{total}\n")
​ self.append_status(f"Proceso de publicación inmediata finalizado.
{processed}/{total} publicados.\n")

​ elif mode == "schedule":


​ self.append_status("Programando publicaciones...\n")
​ from schedule_calculator import calculate_publish_schedule
​ schedule = calculate_publish_schedule(total)
​ for i, file in enumerate(files):
​ if self.stop_event.is_set():
​ break
​ publish_date = schedule[i]
​ result = process_single_file(file, config, publish_date)
​ processed += 1 if result else 0
​ self.update_progress(i + 1, total)
​ self.append_status(f"Progreso: {i + 1}/{total}\n")
​ self.append_status(f"Programación finalizada. {processed}/{total}
programados.\n")
​ elif mode == "queue":
​ self.append_status("Agregando archivos a la cola...\n")
​ from database import track_new_file, is_file_processed
​ added = 0
​ for i, file in enumerate(files, 1):
​ if self.stop_event.is_set():
​ break
​ if not is_file_processed(file['id']):
​ track_new_file(file['id'], file['name'], None)
​ added += 1
​ self.update_progress(i, total)
​ self.append_status(f"Progreso: {i}/{total}\n")
​ self.append_status(f"Archivos agregados a la cola: {added}/{total}\n")
​ else:
​ self.append_status("Modo no reconocido.\n")

​ except Exception as e:
​ print(f"Error en run_mode: {e}")
​ self.append_status(f"Error: {str(e)}\n")
​ finally:
​ self.start_btn.config(state=[Link])
​ self.stop_btn.config(state=[Link])
​ self.progress_var.set(0)

​ def update_progress(self, current, total):


​ percent = (current / total) * 100 if total > 0 else 0
​ self.progress_var.set(percent)
​ self.progress_bar.update()

​ def append_status(self, msg):


​ self.status_text.config(state=[Link])
​ self.status_text.insert([Link], msg)
​ self.status_text.see([Link])
​ self.status_text.config(state=[Link])

if __name__ == "__main__":
​ print("Ejecutando bloque principal de GUI...")
​ app = AutoPostGUI()
​ [Link]()

[Link]:
import os
import json
import time
import logging # Nuevo: logging
from datetime import datetime, timedelta
import pytz
from google_drive_api import list_files_in_drive, get_file_metadata
from exe_io_api import shorten_link
from wordpress_api import create_post, upload_featured_image
from image_processor import get_featured_image_path
from jinja2 import Environment, FileSystemLoader
from database import init_db, track_new_file, is_file_processed, mark_as_published,
save_config, load_config
from schedule_calculator import calculate_publish_schedule
from openrouter_api import get_brief_description

# --- Configuración de logging ---


[Link](
​ filename='[Link]',
​ level=[Link],
​ format='%(asctime)s %(levelname)s: %(message)s'
)
logger = [Link](__name__)

def load_brand_images():
​ """Carga el mapeo de imágenes por marca desde el archivo JSON"""
​ try:
​ with open('brand_images.json', 'r') as f:
​ return [Link](f)
​ except Exception as e:
​ [Link](f"Error cargando brand_images.json: {str(e)}")
​ print(f"Error cargando brand_images.json: {str(e)}")
​ # Valores por defecto mínimos
​ return {
​ "default": {
​ "schematic": 4151,
​ "boardview": 4150
​ }
​ }

def config_wizard():
​ """Asistente interactivo para configuración"""
​ print("\n" + "=" * 50)
​ print("CONFIGURACIÓN DE PUBLICACIÓN PROGRAMADA")
​ print("=" * 50)
​ folder_id = input("\nID de la carpeta de Google Drive: ").strip()

​ print("\nSeleccione una marca:")


​ brands = ["Asus", "Dell", "HP", "Lenovo", "Apple", "Otra"]
​ for i, brand in enumerate(brands, 1):
​ print(f"{i}. {brand}")

​ brand_choice = input("\nOpción (número o nombre): ").strip()


​ if brand_choice.isdigit() and 1 <= int(brand_choice) <= len(brands):
​ brand = brands[int(brand_choice) - 1]
​ else:
​ brand = brand_choice if brand_choice else "default"

​ print("\nIngrese IDs de categorías (separados por coma):")


​ print("Ejemplo: 15,23,42")
​ categories = [int([Link]()) for cat in input("Categorías: ").split(",")]

​ print("\nIngrese IDs de etiquetas (separados por coma):")


​ tags = [int([Link]()) for tag in input("Etiquetas: ").split(",")]

​ return {
​ 'folder_id': folder_id,
​ 'brand': brand,
​ 'categories': categories,
​ 'tags': tags
​ }

def process_single_file(file, config, publish_date=None):


​ """Procesa un solo archivo para publicación"""
​ try:
​ brand_images = load_brand_images()
​ env = Environment(loader=FileSystemLoader('.'))
​ template = env.get_template('[Link]')

​ # Procesar nombre del archivo


​ original_name = file['name']
​ cleaned_name = original_name.replace("[[Link]]",
"").strip().replace("_", " ")
​ base_name = [Link](cleaned_name)[0]
​ title = f"{base_name} Schematic" if "Schematic" not in base_name else
base_name.strip()

​ print(f"\nProcesando: {title}")
​ [Link](f"Procesando archivo: {title}")

​ # Obtener metadatos
​ file_meta = get_file_metadata(file['id'])
​ download_link = f"[Link]
​ shortened_link = shorten_link(download_link)
​ print(f"Enlace acortado: {shortened_link}")
​ [Link](f"Enlace acortado para {title}: {shortened_link}")

​ # Determinar tipo de archivo


​ if "boardview" in [Link]() or "board view" in [Link]():
​ file_type = "boardview"
​ else:
​ file_type = "schematic"

​ # Obtener imagen destacada


​ featured_media_id = None
​ image_path = get_featured_image_path(brand_images, config['brand'], file_type)

​ if image_path:
​ featured_media_id = upload_featured_image(image_path, title)
​ # Eliminar archivo temporal
​ [Link](image_path)
​ print(f"Imagen destacada ID: {featured_media_id}")
​ [Link](f"Imagen destacada subida para {title}, ID: {featured_media_id}")
​ else:
​ print("No se pudo obtener imagen destacada")
​ [Link](f"No se pudo obtener imagen destacada para {title}")

​ # Obtener descripción breve usando [Link]


​ api_key = [Link]("OPENROUTER_API_KEY")
​ brief_description = get_brief_description(title, api_key)

​ # Renderizar contenido incluyendo la descripción


​ content = [Link](
​ file_name=title,
​ file_size=f"{file_meta['size']} MB" if file_meta['size'] != 'N/A' else 'Desconocido',
​ mime_type=file_meta['mimeType'],
​ download_link=shortened_link,
​ current_year=[Link]().year,
​ brief_description=brief_description # <-- Nuevo campo integrado
​ )

​ # Crear entrada en WordPress


​ create_post(
​ title=title,
​ content=content,
​ category_ids=config['categories'],
​ featured_media_id=featured_media_id,
​ tag_ids=config['tags'],
​ publish_date=publish_date
​ )

​if publish_date:
​ print(f"Post '{title}' programado para {publish_date.strftime('%Y-%m-%d %H:%M')}")
​ [Link](f"Post '{title}' programado para {publish_date.strftime('%Y-%m-%d
%H:%M')}")
​ else:
​ print(f"Entrada '{title}' publicada con éxito")
​ [Link](f"Entrada '{title}' publicada con éxito")

​ return True
​ except Exception as e:
​ print(f"Error procesando archivo: {str(e)}")
​ [Link](f"Error procesando archivo {[Link]('name', '')}: {str(e)}", exc_info=True)
​ return False

def batch_mode(config=None):
​ """Modo de publicación inmediata de todos los archivos"""
​ if config is None:
​ saved_config = load_config('last_config')
​ if saved_config:
​ use_saved = input("¿Usar configuración guardada? (s/n): ").strip().lower() == 's'
​ if use_saved:
​ config = saved_config
​ else:
​ config = config_wizard()
​ save_config('last_config', config)
​ else:
​ config = config_wizard()
​ save_config('last_config', config)

​ try:
​ files = list_files_in_drive(config['folder_id'])
​ except Exception as e:
​ print(f"Error obteniendo archivos de Google Drive: {str(e)}")
​ [Link](f"Error obteniendo archivos de Google Drive: {str(e)}", exc_info=True)
​ return
​ total = len(files)
​ success = 0

​ for i, file in enumerate(files, 1):


​ try:
​ if not is_file_processed(file['id']):
​ if process_single_file(file, config): # Publica inmediatamente
​ # Marcamos como publicado en la base de datos
​ mark_as_published(file['id'])
​ success += 1
​ print(f"Progreso: {i}/{total} archivos")
​ except Exception as e:
​ print(f"Error procesando archivo {[Link]('name', '')}: {str(e)}")
​ [Link](f"Error procesando archivo en batch_mode: {str(e)}", exc_info=True)

​ print(f"\nProceso completado: {success}/{total} archivos publicados")


​ [Link](f"Proceso batch_mode completado: {success}/{total} archivos publicados")

def schedule_mode(config=None):
​ """Modo de programación de publicaciones"""
​ if config is None:
​ config = config_wizard()
​ save_config('last_config', config)

​ try:
​ files = list_files_in_drive(config['folder_id'])
​ except Exception as e:
​ print(f"Error obteniendo archivos de Google Drive: {str(e)}")
​ [Link](f"Error obteniendo archivos de Google Drive: {str(e)}", exc_info=True)
​ return

​ new_files = [f for f in files if not is_file_processed(f['id'])]

​ if not new_files:
​ print("No hay archivos nuevos para programar")
​ [Link]("No hay archivos nuevos para programar en schedule_mode")
​ return

​ # Calcular programación
​ schedule = calculate_publish_schedule(len(new_files))

​ print(f"\nProgramando {len(new_files)} posts:")


​ print("Se publicarán 5 posts cada día a las 8:00 AM")
​ for i, file in enumerate(new_files):
​ try:
​ track_new_file(file['id'], file['name'], schedule[i])

​ if process_single_file(file, config, schedule[i]):


​ print(f"[{i + 1}/{len(new_files)}] Programado para {schedule[i].strftime('%Y-%m-%d
%H:%M')}")
​ [Link](f"Archivo {file['name']} programado para
{schedule[i].strftime('%Y-%m-%d %H:%M')}")
​ else:
​ print(f"[{i + 1}/{len(new_files)}] Error al programar")
​ [Link](f"Error al programar archivo {file['name']}")
​ except Exception as e:
​ print(f"Error procesando archivo {[Link]('name', '')}: {str(e)}")
​ [Link](f"Error procesando archivo en schedule_mode: {str(e)}", exc_info=True)

​ print("\n¡Programación completada! WordPress publicará automáticamente según el


calendario")
​ [Link]("Programación completada en schedule_mode")

def queue_mode(config=None):
​ """Agrega archivos a la cola sin publicar"""
​ if config is None:
​ folder_id = input("ID de la carpeta de Google Drive: ").strip()
​ else:
​ folder_id = config['folder_id']
​ try:
​ files = list_files_in_drive(folder_id)
​ except Exception as e:
​ print(f"Error obteniendo archivos de Google Drive: {str(e)}")
​ [Link](f"Error obteniendo archivos de Google Drive en queue_mode: {str(e)}",
exc_info=True)
​ return

​ added = 0
​ for file in files:
​ try:
​ if not is_file_processed(file['id']):
​ # Programar para fecha mínima (se actualizará después)
​ track_new_file(file['id'], file['name'], [Link]())
​ added += 1
​ except Exception as e:
​ print(f"Error agregando archivo {[Link]('name', '')} a la cola: {str(e)}")
​ [Link](f"Error agregando archivo a la cola en queue_mode: {str(e)}",
exc_info=True)

​ print(f"\nSe agregaron {added} archivos a la cola de publicación")


​ [Link](f"Se agregaron {added} archivos a la cola de publicación en queue_mode")

def main_menu():
​ """Muestra el menú principal"""
​ print("\n" + "=" * 50)
​ print("SISTEMA DE PUBLICACIÓN AUTOMÁTICA WORDPRESS")
​ print("=" * 50)
​ print("1. Publicación inmediata (todos los archivos ahora)")
​ print("2. Programar publicaciones (5 por día a las 8 AM)")
​ print("3. Agregar archivos a la cola (sin publicar aún)")
​ print("4. Salir")

​ choice = input("\nSeleccione una opción: ").strip()


​ return choice

def main():
​ try:
​ init_db()
​ except Exception as e:
​ print(f"Error inicializando la base de datos: {str(e)}")
​ [Link](f"Error inicializando la base de datos: {str(e)}", exc_info=True)
​ return

​ while True:
​ choice = main_menu()

​ if choice == '1':
​ batch_mode()
​ elif choice == '2':
​ schedule_mode()
​ elif choice == '3':
​ queue_mode()
​ elif choice == '4':
​ print("Saliendo del sistema...")
​ [Link]("Sistema finalizado por el usuario")
​ break
​ else:
​ print("Opción inválida, por favor intente nuevamente")
​ [Link](f"Opción inválida seleccionada: {choice}")
if __name__ == "__main__":
​ main()

image_processor.py:
import os
import requests
import tempfile

# Configuración
WP_URL = '[Link]

def download_image_from_wordpress(image_id):
​ """
​ Descarga una imagen desde WordPress y devuelve la ruta temporal
​ """
​ try:
​ media_url = f"{WP_URL}/wp-json/wp/v2/media/{image_id}"
​ response = [Link](media_url, timeout=15)

​ if response.status_code == 200:
​ image_url = [Link]().get('source_url')
​ temp_path = [Link](suffix='.jpg')

​ # Descargar la imagen
​ img_response = [Link](image_url, stream=True, timeout=15)
​ img_response.raise_for_status()

​ with open(temp_path, 'wb') as f:


​ for chunk in img_response.iter_content(chunk_size=8192):
​ [Link](chunk)
​ return temp_path
​ else:
​ print(f"Error obteniendo imagen ID {image_id}: {response.status_code}")
​ return None
​ except Exception as e:
​ print(f"Error descargando imagen ID {image_id}: {str(e)}")
​ return None

def get_image_id_for_brand(brand_images, brand, file_type):


​ """
​ Obtiene el ID de imagen adecuado según la marca y tipo
​ """
​ # Normalizar la marca
​ normalized_brand = [Link]().strip() if brand else "default"

​ # Buscar imagen específica para la marca


​ if normalized_brand in brand_images:
​ return brand_images[normalized_brand].get(file_type)

​ # Si no se encuentra, usar las imágenes genéricas


​ return brand_images['default'].get(file_type)

def get_featured_image_path(brand_images, brand, file_type):


​ """
​ Obtiene la ruta de la imagen destacada adecuada
​ """
​ # Obtener ID de imagen
​ image_id = get_image_id_for_brand(brand_images, brand, file_type)

​ if not image_id:
​ print(f"No se encontró imagen para {brand}/{file_type}")
​ return None

​ # Descargar la imagen
​ return download_image_from_wordpress(image_id)

google_drive_api.py:
import os
from google.oauth2 import service_account
from [Link] import build
from [Link] import HttpError

SCOPES = ['[Link]
SERVICE_ACCOUNT_FILE = [Link]("SERVICE_ACCOUNT_FILE", 'service_account.json')

try:
​ credentials = service_account.Credentials.from_service_account_file(
​ SERVICE_ACCOUNT_FILE, scopes=SCOPES)
​ service = build('drive', 'v3', credentials=credentials)
except Exception as e:
​ print(f"Error inicializando API de Google Drive: {str(e)}")
​ service = None

def list_files_in_drive(folder_id):
​ if not service:
​ return []

​ try:
​ results = [Link]().list(
​ q=f"'{folder_id}' in parents and trashed = false",
​ fields="files(id, name, mimeType, size)",
​ pageSize=100
​ ).execute()
​ return [Link]('files', [])
​ except HttpError as e:
​ print(f"Google API error: {str(e)}")
​ return []
​ except Exception as e:
​ print(f"Error inicializando API de Google Drive: {e}")
​ # Si tienes acceso a la respuesta, imprímela aquí
​ # print([Link])
​ return []

def get_file_metadata(file_id):
​ if not service:
​ return {'size': 'N/A', 'mimeType': 'N/A'}

​ try:
​ file = [Link]().get(
​ fileId=file_id,
​ fields="size,mimeType"
​ ).execute()

​ size_bytes = [Link]('size', '0')


​ if size_bytes.isdigit():
​ size_mb = round(int(size_bytes) / (1024 * 1024), 2)
​ else:
​ size_mb = 'N/A'

​ return {
​ 'size': size_mb,
​ 'mimeType': [Link]('mimeType', 'application/octet-stream')
​ }
​ except HttpError as e:
​ print(f"Google API error: {str(e)}")
​ return {'size': 'N/A', 'mimeType': 'N/A'}
​ except Exception as e:
​ print(f"General error: {str(e)}")
​ return {'size': 'N/A', 'mimeType': 'N/A'}

exe_io_api.py:
import requests
import os

API_KEY = [Link]("EXE_IO_API_KEY", "172331805db271a6c5cd30171298908af2db2b88")

def shorten_link(long_url):
​ api_url = f"[Link]

​ try:
​ response = [Link](api_url, timeout=15)
​ response.raise_for_status()

​ json_response = [Link]()
​ if json_response.get('status') == 'success':
​ return json_response.get('shortenedUrl', long_url)
​ else:
​ print(f"API error: {json_response.get('message', 'Unknown error')}")
​ return long_url
​ except [Link] as e:
​ print(f"Request error: {str(e)}")
​ return long_url
​ except Exception as e:
​ print(f"General error: {str(e)}")
​ return long_url
[Link]:
import sqlite3
import json
import os
from contextlib import contextmanager
from datetime import datetime

DB_NAME = "wp_scheduler.db"

def init_db():
​ """Inicializa la base de datos"""
​ with db_connection() as conn:
​ [Link]("""
​ CREATE TABLE IF NOT EXISTS scheduled_posts (
​ id INTEGER PRIMARY KEY AUTOINCREMENT,
​ drive_file_id TEXT NOT NULL UNIQUE,
​ title TEXT NOT NULL,
​ scheduled_date TIMESTAMP NOT NULL,
​ status TEXT DEFAULT 'scheduled'
​ )
​ """)

​ [Link]("""
​ CREATE TABLE IF NOT EXISTS app_config (
​ key TEXT PRIMARY KEY,
​ value TEXT
​ )
​ """)

@contextmanager
def db_connection():
​ """Manejador de conexión a la base de datos"""
​ conn = [Link](DB_NAME)
​ conn.row_factory = [Link]
​ try:
​ yield conn
​ finally:
​ [Link]()

def track_new_file(file_id, title, scheduled_date):


​ """Registra un nuevo archivo en la base de datos"""
​ with db_connection() as conn:
​ [Link]("""
​ INSERT OR REPLACE INTO scheduled_posts
​ (drive_file_id, title, scheduled_date, status)
​ VALUES (?, ?, ?, 'scheduled')
​ """, (file_id, title, scheduled_date.isoformat()))

def is_file_processed(file_id):
​ """Verifica si un archivo ya ha sido procesado"""
​ with db_connection() as conn:
​ cursor = [Link]("""
​ SELECT 1 FROM scheduled_posts WHERE drive_file_id = ?
​ """, (file_id,))
​ return [Link]() is not None

def mark_as_published(file_id):
​ """Marca un archivo como publicado en la base de datos"""
​ with db_connection() as conn:
​ [Link]("""
​ UPDATE scheduled_posts
​ SET status = 'published'
​ WHERE drive_file_id = ?
​ """, (file_id,))

def save_config(key, value):


​ """Guarda configuración en la base de datos"""
​ with db_connection() as conn:
​ [Link]("""
​ INSERT OR REPLACE INTO app_config (key, value)
​ VALUES (?, ?)
​ """, (key, [Link](value)))

def load_config(key):
​ """Carga configuración desde la base de datos"""
​ with db_connection() as conn:
​ cursor = [Link]("""
​ SELECT value FROM app_config WHERE key = ?
​ """, (key,))
​ row = [Link]()
​ return [Link](row['value']) if row else None

def get_published_files():
​ """Obtiene todos los archivos publicados"""
​ with db_connection() as conn:
​ cursor = [Link]("""
​ SELECT drive_file_id, title FROM scheduled_posts
​ WHERE status = 'published'
​ """)
​ return [dict(row) for row in [Link]()]

def get_scheduled_files():
​ """Obtiene todos los archivos programados"""
​ with db_connection() as conn:
​ cursor = [Link]("""
​ SELECT drive_file_id, title, scheduled_date FROM scheduled_posts
​ WHERE status = 'scheduled'
​ """)
​ return [dict(row) for row in [Link]()]

brand_images.json:
{
"default": {
​ "schematic": 4150,
​ "boardview": 4151
},
"amd": {
​ "schematic": 4547,
​ "boardview": 4546
},
"toshiba": {
​ "schematic": 4567,
​ "boardview": 4566
},
"sony": {
​ "schematic": 4565,
​ "boardview": 4564
},
"msi": {
​ "schematic": 4563,
​ "boardview": 4562
},
"lg": {
​ "schematic": 4561,
​ "boardview": 4560
},
"lenovo": {
​ "schematic": 4559,
​ "boardview": 4558
},
"gigabyte": {
​ "schematic": 4557,
​ "boardview": 4556
},
"foxconn": {
​ "schematic": 4555,
​ "boardview": 4554
},
"dell": {
​ "schematic": 4553,
​ "boardview": 4552
},
"asus": {
​ "schematic": 4551,
​ "boardview": 4550
},
"asrock": {
​ "schematic": 4549,
​ "boardview": 4548
}
}

openrouter_api.py:
import requests

def get_brief_description(file_name, api_key):


​ prompt = (
​ f"Genera una breve descripción del equipo al que pertenece el archivo de diagrama
'{file_name}'. "
​ "Describe el tipo de equipo y su función en menos de 40 palabras."
​ )
​ url = "[Link]
​ headers = {
​ "Authorization": f"Bearer {api_key}",
​ "Content-Type": "application/json"
​ }
​ data = {
​ "model": "openai/gpt-3.5-turbo", # Puedes cambiar el modelo si tienes acceso a otro
​ "messages": [
​ {"role": "user", "content": prompt}
​ ]
​ }
​ try:
​ response = [Link](url, headers=headers, json=data)
​ response.raise_for_status()
​ result = [Link]()
​ return result["choices"][0]["message"]["content"].strip()
​ except Exception as e:
​ print(f"Error consultando [Link]: {e}")
​ return ""

schedule_calculator.py:
from datetime import datetime, timedelta
import pytz

def calculate_publish_schedule(total_files, files_per_day=5):


​ """Calcula fechas de publicación para cada archivo"""
​ # Obtener fecha/hora actual en UTC
​ now = [Link]([Link])

​ # Determinar fecha de inicio (próximo día a las 8 AM)


​ if [Link] < 8:
​ start_date = [Link](hour=8, minute=0, second=0, microsecond=0)
​ else:
​ start_date = (now + timedelta(days=1)).replace(hour=8, minute=0, second=0,
microsecond=0)

​ schedule = []
​ current_date = start_date
​ file_count = 0

​ for _ in range(total_files):
​ # Solo 5 archivos por día
​ if file_count >= files_per_day:
​ current_date += timedelta(days=1)
​ file_count = 0

​ [Link](current_date)
​ file_count += 1

​ return schedule

service_account.json:
{
"type": "service_account",
"project_id": "automawp",
"private_key_id": "1bd7b7844ff5ed89a868600b0419d08b4481c99a",
"private_key": "-----BEGIN PRIVATE
KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCm4viy98DbcDP
X\n8vAQjWVpza953ROul6Qrnk6wcPVRon3Gb1+Eul30kNuXCX3MDiNKnK7Yr7gwCw5K\njKI8x
nS5RoKgkQysLpltBYApPsNdLAajXdPvNlGp2d8u/aI19yBdpBMNqPCOvYwD\nkDOQ/uDq/xK8+
Ds0TQgGkpeOTXpwVl2Q23zq7YXY+OysOqcWB7wqrRGHt4slbtqc\n+GuW0JEqQho1CkWNuf
2bVO77YnqWRroHwFD5QdxHmHPmlspmuUIfFoF2VxxWnFbZ\n+R5DZBWqs9wOPZigSdPxlW
O7ss//kFf090FAKyZzrmlkfzGvD85PI2QEOVThNTtM\nZ4jerInHAgMBAAECggEARU99z8Jy8BA
9D+KfTpiS9mrNBrVJL9ld/xmb/86iLDnq\nt8fYqWTHkcO515RDEUUL5jiFq4vrWNS2pkKbPNeFc
c91gB9N3pLp+KvwqvCcre0T\ngrT8yNxL9Vl9xmPjgzs0T3/Nw/4WjonthStWJ1Gb8y1lyGLmH9Y
gT66BUEIgaFfi\nTktBFQtJVIP0c+DK5xMfGBY6rZPzNbVRKAtwlyZY9N/+Ux01DT0z8lOBlIWck0
XV\n1Uby3LjZjPyl+VioMwk2gKu8SNSJmoVLcXb8aC6PVXreCfwbHBc8cH+DYVNZYrIZ\n/oViW
PkHkZyYwY9ViTPUPNKIus4uayhT/HIg6nGn/QKBgQDZz7Z2uZrjaN24vj9m\nNu2wsBacusrA9Q
iE61JWf7g8mWr+S8NnJUSlcW/jTAmVk2LT0ieNxrrAE5QcmAq8\ny5bBEbuT0pcyrhhmeBiIOj3K
PKflHz++09KOj9SjpslWNwSn55IHlQB8nOEUOTgJ\n4PPI7YSFeLmOqudZ/gCXACNJjQKBgQD
EJYqqGhaLeMyA9NmKu6vm1WXw3SJHtiwK\nor2HWIT05aFQKpxlGlIl02rhVRKd45SicBwM+xr
KamgE/biH3B87Dca0bk4qMoV0\nHvHU/5bfPodnuCvKWAT9pky43omU0GFlCeXTewuVP9go1d
uInnG/LtVwUy48rc+r\nChcBU4nJowKBgEmtqsJEjLFqcr4glN+lqwe+uRdxVE6rVKNp2uNpts/A8e
FtjX7h\nq3PviHKx8PgI8EBI8ZpS5C8/MDXv9InisG9E6Vfav8yj+ZsH+vxx/x7fH8gwpuaN\nTsdsvf
hR3f9tAwUtH0ezToPYqYamyF8HGlAYkGB58wLgqvvVISvzHvytAoGADC8k\n0PIz6lADrBMJnG
OSBxHEdlTWN0wqqjJ3wXuPLsNA5QDwnBspdS4P2pV1RZeIeRqr\ns2a5wE8hzQWRu+bBjeK
KMFUVx+tBfPgbH5cM4Ss6MAgxuC0FilgVNfqk4ziahDM8\nMonXK1s+6zJ1o6Pr/utC2Oph2eU
WHFLRIQT2by8CgYEAucSKrYwz7tJvX+1D7c+Q\nfrv1nufMEAKshJ5hDo8c8CuA//xlDQ29iaPB
DQCmuXeIxmzvv5DOQKFiJrhmx8cz\nG4niXfLNfP1hJKYKxnPUA3SmXZEgMZPrkBAWqoV2nr
VmuTZoZFrxTQ0HnAjkjtS5\nvMOU+YTL8wnIc3HcRe7dLIo=\n-----END PRIVATE KEY-----\n",
"client_email": "wordpressautopost@[Link]",
"client_id": "105639058406733556220",
"auth_uri": "[Link]
"token_uri": "[Link]
"auth_provider_x509_cert_url": "[Link]
"client_x509_cert_url":
"[Link]
[Link]",
"universe_domain": "[Link]"
}

[Link]:
<!-- wp:more -->
<!--more-->
<!-- /wp:more -->

<!-- wp:paragraph -->


<p><strong>{{ file_name }}</strong>> files free download</p>
<!-- /wp:paragraph -->

<!-- wp:table -->


<figure class="wp-block-table">
​ <table class="file-info">
​ <tbody>
​ <tr>
​ <th>File Name</th>
​ <td>{{ file_name }}</td>
​ </tr>
​ <tr>
​ <th>File Size</th>
​ <td>{{ file_size }}</td>
​ </tr>
​ <tr>
​ <th>File Type</th>
​ <td>{{ mime_type }}</td>
​ </tr>
​ <tr>
​ <th>Download Server</th>
​ <td>Google Drive</td>
​ </tr>
​ </tbody>
​ </table>
</figure>
<!-- /wp:table -->

<!-- wp:buttons {"layout":{"type":"flex","justifyContent":"center"}} -->


<div class="wp-block-buttons">
​ <!-- wp:button {"className":"download-button"} -->
​ <div class="wp-block-button download-button">
​ <a class="wp-block-button__link" href="{{ download_link }}" target="_blank">&#128229;
Download Schematic</a>
​ </div>
​ <!-- /wp:button -->
</div>
<!-- /wp:buttons -->

<!-- wp:html -->


<div class="ads-section">
​ <script type="text/javascript">
​ atOptions = {
​ 'key' : 'aa66397b59f92ae2ab4c2430dd928887',
​ 'format' : 'iframe',
​ 'height' : 90,
​ 'width' : 728,
​ 'params' : {}
​ };
​ </script>
​ <script type="text/javascript"
src="//[Link]/aa66397b59f92ae2ab4c2430dd928887/[Link]"></script>
</div>
<!-- /wp:html -->

<!-- wp:group {"className":"note-box"} -->


<div class="wp-block-group note-box">
​ <!-- wp:paragraph -->
​ <p><strong>Note:</strong> This schematic is provided for educational and repair
purposes only. Always use these resources responsibly.</p>
​ <!-- /wp:paragraph -->
</div>
<!-- /wp:group -->

<!-- wp:group {"className":"help-box"} -->


<div class="wp-block-group help-box">
​ <!-- wp:paragraph -->
​ <p><strong>Need Help?</strong> If the download link does not work or you need a
different schematic, please <a href="[Link]
us</a>.</p>
​ <!-- /wp:paragraph -->
</div>
<!-- /wp:group -->

<!-- wp:heading {"level":2} -->


<h2>About this Schematic</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->


<p>The <strong>{{ file_name }}</strong> is a vital resource for technicians, engineers, and
enthusiasts working on this device. It includes detailed circuit diagrams and board layouts,
making repairs and diagnostics much easier.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->


<p>Open the .CAD file with <a
href="[Link] or <a
href="[Link]
>Altium 365</a>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->


<p>Open the PDF file with <a href="[Link] Reader</a></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->


<p><strong>Legal Disclaimer</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->


<p>Manufacturer does not publicly release schematics or boardview files. These resources are
shared to aid <strong>responsible repair efforts</strong>. Always comply with local laws and
avoid unauthorized distribution. The information provided here is based on available
specifications and features at the time of writing. Always check the official product
documentation for the most up-to-date details. <strong>Use these schematics at your own
risk.</strong></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->


<p>Struggling with a stubborn repair? Share your story below! <br><strong>#PCBRepair
#TechCommunity</strong></p>
<!-- /wp:paragraph -->

<!-- wp:list -->


<ul>
​ <li>High-quality PDF format</li>
​ <li>Direct download from Google Drive</li>
​ <li>Free access - no registration required</li>
​ <li>Trusted by the repair community</li>
</ul>
<!-- /wp:list -->

<!-- wp:html -->


<style>
​ .schematic-container {
​ max-width: 800px;
​ margin: 0 auto;
​ padding: 20px;
​ font-family: Arial, sans-serif;
​ color: #333;
​ }

​ .file-info table {
​ width: 100%;
​ border-collapse: collapse;
​ margin-bottom: 30px;
​ box-shadow: 0 2px 8px rgba(0,0,0,0.05);
​ }

​ .file-info th, .file-info td {


​ padding: 12px 15px;
​ text-align: left;
​ border-bottom: 1px solid #e1e1e1;
​ }

​ .file-info th {
​ background-color: #f8f9fa;
​ font-weight: bold;
​ color: #34495e;
​ width: 30%;
​ }

​ .download-button .wp-block-button__link {
​ display: inline-block;
​ background-color: #27ae60;
​ color: white;
​ font-size: 20px;
​ font-weight: bold;
​ padding: 15px 40px;
​ border-radius: 6px;
​ text-decoration: none;
​ box-shadow: 0 4px 12px rgba(39, 174, 96, 0.2);
​ transition: all 0.3s ease;
​ }

​ .download-button .wp-block-button__link:hover {
​ background-color: #219653;
​ transform: translateY(-2px);
​ box-shadow: 0 6px 15px rgba(39, 174, 96, 0.3);
​ }

​ .note-box {
​ padding: 15px 20px;
​ border-radius: 6px;
​ margin-bottom: 25px;
​ background-color: #fff3cd;
​ border-left: 4px solid #ffc107;
​ color: #856404;
​ }

​ .help-box {
​ padding: 15px 20px;
​ border-radius: 6px;
​ margin-bottom: 25px;
​ background-color: #e8f4fd;
​ border-left: 4px solid #3498db;
​ color: #2c3e50;
​ }

​ .help-box a {
​ color: #2980b9;
​ text-decoration: underline;
​ }

​ .wp-block-list li {
​ margin-bottom: 8px;
​ line-height: 1.5;
​ }
</style>
<!-- /wp:html -->

<!-- wp:paragraph -->


<p><strong>Descripción:</strong> {{ brief_description }}</p>
<!-- /wp:paragraph →

[Link]:
# WordPress Configuration
WP_URL=[Link]
WP_USER=auto
WP_PASSWORD="cOF3 u5nC DSG7 HUO8 sTaX LiAm"

# Google Drive API


SERVICE_ACCOUNT_FILE=service_account.json

OPENROUTER_API_KEY=sk-or-v1-9254a9063f41e7cf13988c62475000da52185caa05f353ac5
c70e3035075d078

wordpress_api.py:
import pytz
import requests
import os
from datetime import datetime

# Configuración desde variables de entorno


WP_URL = [Link]("WP_URL", "[Link]
WP_USER = [Link]("WP_USER", "auto")
WP_PASSWORD = [Link]("WP_PASSWORD", "cOF3 u5nC DSG7 HUO8 sTaX LiAm")
WP_MEDIA_URL = WP_URL.replace('/posts', '/media')

def upload_featured_image(image_path, title):


​ """Sube la imagen destacada a WordPress y devuelve su ID."""
​ try:
​ with open(image_path, 'rb') as img:
​ file_name = [Link](image_path)
​ headers = {'Content-Disposition': f'attachment; filename={file_name}'}
​ response = [Link](
​ WP_MEDIA_URL,
​ headers=headers,
​ files={'file': (file_name, img, 'image/jpeg')},
​ auth=(WP_USER, WP_PASSWORD),
​ timeout=30
​ )

​ if response.status_code in (200, 201):


​ return [Link]()['id']
​ else:
​ print(f"Error al subir imagen ({response.status_code}): {[Link]}")
​ return None
​ except Exception as e:
​ print(f"Excepción al subir imagen: {str(e)}")
​ return None

def create_post(title, content, category_ids, featured_media_id, tag_ids, publish_date=None):


​ """Crea una entrada en WordPress, puede ser programada si se pasa publish_date"""
​ post_data = {
​ 'title': title,
​ 'content': content,
​ 'status': 'publish' if publish_date is None else 'future',
​ 'categories': category_ids,
​ 'tags': tag_ids,
​ 'featured_media': featured_media_id,
​ }

​ # Añadir fecha de publicación si se especifica


​ if publish_date:
​ # Convertir a zona horaria de WordPress (UTC)
​ publish_date_utc = publish_date.astimezone([Link])
​ post_data['date'] = publish_date_utc.isoformat()

​ try:
​ response = [Link](
​ WP_URL,
​ json=post_data,
​ auth=(WP_USER, WP_PASSWORD),
​ timeout=30
​ )
​ if response.status_code in (200, 201):
​ return [Link]()
​ else:
​ raise Exception(f"Error {response.status_code}: {[Link]}")
​ except Exception as e:
​ raise Exception(f"Error creando entrada: {str(e)}")

También podría gustarte