feat: initial commit - Instagram giveaway analyzer & auto-inscriber

This commit is contained in:
Dev Timofijo
2026-08-30 20:41:20 +00:00
commit 3a3290cfff
6 changed files with 448 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# Dependencies
__pycache__/
*.pyc
*.pyo
*.pyd
venv/
.env
*.env*
# IDE
.vscode/
.idea/
# OS
.DS_Store
+109
View File
@@ -0,0 +1,109 @@
"""Analizador de IA local simple para detección de sorteos."""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from config import GIVEAWAY_KEYWORDS, AI_MODE
class GiveawayAnalyzer:
"""Analizador simple basado en reglas para detectar sorteos en texto."""
def __init__(self, mode="rule_based"):
self.mode = mode
self.keywords = GIVEAWAY_KEYWORDS
def is_giveaway(self, text):
"""
Determinar si un texto corresponde a un sorteo.
Args:
text: Texto de la descripción o caption de Instagram
Returns:
bool: True si parece un sorteo
"""
if not text:
return False
text_lower = text.lower()
# Contar coincidencias de palabras clave
matches = sum(1 for kw in self.keywords if kw.lower() in text_lower)
# Heurística: si hay al menos 2 palabras clave, es probable que sea un sorteo
if matches >= 2:
return True
# Patrones más específicos
giveaway_patterns = [
"#sorteo" in text_lower,
"#giveaway" in text_lower,
"sorteo" in text_lower and ("ganar" in text_lower or "win" in text_lower),
"participa" in text_lower,
"etiqueta" in text_lower or "tag" in text_lower,
"comenta" in text_lower,
]
if any(giveaway_patterns):
return True
return False
def extract_requirements(self, text):
"""
Extraer los requisitos del sorteo del texto.
Args:
text: Texto del sorteo
Returns:
dict: Diccionario con requisitos extraídos
"""
requirements = {
"follow": False,
"like": False,
"comment": False,
"tag_friends": False,
"hashtag": None,
"username": None
}
text_lower = text.lower()
if "follow" in text_lower or "seguir" in text_lower:
requirements["follow"] = True
if "like" in text_lower or "dar like" in text_lower or "corazón" in text_lower:
requirements["like"] = True
if "comment" in text_lower or "comenta" in text_lower:
requirements["comment"] = True
if "tag" in text_lower or "etiqueta" in text_lower or "menciona" in text_lower:
requirements["tag_friends"] = True
# Buscar hashtag
import re
hashtag_match = re.search(r"#(\w+)", text)
if hashtag_match:
requirements["hashtag"] = hashtag_match.group(1)
# Buscar usuario (@usuario)
user_match = re.search(r"@(\w+)", text)
if user_match:
requirements["username"] = user_match.group(1)
return requirements
def analyze(self, text):
"""
Análisis completo de un texto de sorteo.
Args:
text: Texto a analizar
Returns:
dict: Resultado del análisis
"""
return {
"is_giveaway": self.is_giveaway(text),
"requirements": self.extract_requirements(text),
"text": text[:100] + "..." if len(text) > 100 else text
}
+23
View File
@@ -0,0 +1,23 @@
# Instagram Giveaway Sorteo Configuration
import os
from dotenv import load_dotenv
load_dotenv()
# Configuración de Instagram
INSTAGRAM_USERNAME = os.getenv("INSTAGRAM_USERNAME", "tu_usuario")
INSTAGRAM_PASSWORD = os.getenv("INSTAGRAM_PASSWORD", "tu_contraseña")
TARGET_USERS = os.getenv("TARGET_USERS", "").split(",") if os.getenv("TARGET_USERS") else []
# Palabras clave para detectar sorteos
GIVEAWAY_KEYWORDS = [
"sorteo", "giveaway", "ganar", "win", "ganando", "ganada",
"premio", "premios", "comparte", "share", "etiquetar", "tag",
"comenta", "comment", "follow", "seguir"
]
# Configuración de IA local (reglas simples)
AI_MODE = "rule_based" # o "ollama" si tiene modelo local configurado
# Comentario automático
AUTO_COMMENT = "¡Participo! #sorteo #ganar"
+115
View File
@@ -0,0 +1,115 @@
"""Módulo para inscribirse automáticamente en sorteos."""
import time
import random
from bs4 import BeautifulSoup
class GiveawayInscriber:
"""Clase para inscribir automáticamente en sorteos de Instagram."""
def __init__(self, scraper, analyzer, comment="¡Participo! #sorteo"):
self.scraper = scraper
self.analyzer = analyzer
self.comment = comment
self.successful_inscriptions = 0
self.failed_attempts = 0
def inscription_comment(self, post_url, target_comment=None):
"""
Publicar un comentario en una publicación de sorteo.
Args:
post_url: URL de la publicación
target_comment: Comentario personalizado (usa self.comment por defecto)
Returns:
bool: Éxito o no
"""
comment = target_comment or self.comment
try:
# Nota: Instagram no permite comentarios fáciles sin autenticación adecuada
# Esta es la estructura; la implementación completa requiere
# manejo de sesiones CSRF, tokens, etc.
print(f"📝 Intentando comentar en: {post_url}")
print(f" Comentario: {comment}")
# En un implementación completa, aquí se tendría:
# - CSRF token extraction
# - Session cookie management
# - POST request al endpoint de comentarios
# - Manejo de respuestas y rate limiting
# Simulación para demostración
time.sleep(random.uniform(1, 3))
self.successful_inscriptions += 1
print("✅ Comentario publicado (simulación)")
return True
except Exception as e:
self.failed_attempts += 1
print(f"❌ Error al comentar: {e}")
return False
def auto_enter(self, posts, min_keyword_match=2):
"""
Analizar publicaciones e inscribirse en las que son sorteos.
Args:
posts: Lista de publicaciones (diccionarios con 'caption', 'url', etc.)
min_keyword_match: Mínimo de coincidencias de palabras clave
Returns:
list: Publicaciones en las que se inscribió
"""
inscribed = []
for post in posts:
caption = post.get("caption", "")
url = post.get("url", "")
if not caption or not url:
continue
# Analizar con la IA local
analysis = self.analyzer.analyze(caption)
if analysis["is_giveaway"]:
print(f"\n🎯 Sorteo detectado en: {url}")
print(f" Requerimientos: {analysis['requirements']}")
# Inscribirse (comentar)
if self.inscription_comment(url):
inscribed.append({
"url": url,
"caption": caption,
"requirements": analysis["requirements"]
})
else:
print(f"📄 Publicación normal (no sorteo): {url[:50] if url else 'N/A'}...")
return inscribed
def run_on_hashtag(self, hashtag, post_count=20):
"""
Ejecutar en un hashtag específico.
Args:
hashtag: Hashtag a buscar (ej: "sorteo")
post_count: Número de publicaciones a revisar
"""
print(f"🔍 Buscando sorteos con hashtag: #{hashtag}")
posts = self.scraper.search_hashtag(hashtag, post_count)
if posts:
print(f"📊 Encontradas {len(posts)} publicaciones")
inscribed = self.auto_enter(posts)
print(f"\n✅ Inscripciones completadas: {len(inscribed)}")
return inscribed
else:
print("❌ No se encontraron publicaciones")
return []
if __name__ == "__main__":
print("🐍 Módulo de inscripción directo")
print("Usa: from inscriber import GiveawayInscriber")
+88
View File
@@ -0,0 +1,88 @@
"""Aplicación principal - Instagram Giveaway Sorteo Analyzer & Inscriber."""
import sys
import os
from dotenv import load_dotenv
# Cargar variables de entorno
load_dotenv()
from scraping.instagram import InstagramScraper
from ai.analyzer import GiveawayAnalyzer
from inscriber import GiveawayInscriber
def main():
print("=" * 60)
print("🎯 Instagram Sorteo Analyzer & Auto-Inscriber")
print("=" * 60)
# Verificar configuración
username = os.getenv("INSTAGRAM_USERNAME")
password = os.getenv("INSTAGRAM_PASSWORD")
if not username or not password:
print("⚠️ Configuración faltante.")
print(" Copia .env y establece INSTAGRAM_USERNAME y INSTAGRAM_PASSWORD")
print(" Ejemplo: echo 'INSTAGRAM_USERNAME=tu_usuario' > .env")
print(" echo 'INSTAGRAM_PASSWORD=tu_contraseña' >> .env")
return
# Inicializar componentes
print("\n🔧 Inicializando componentes...")
scraper = InstagramScraper(username=username, password=password)
analyzer = GiveawayAnalyzer(mode="rule_based")
inscriber = GiveawayInscriber(scraper=scraper, analyzer=analyzer)
print("✅ Componentes listos")
# Modo de operación
print("\n📋 Modos disponibles:")
print(" 1. Buscar por hashtag (#sorteo, #giveaway)")
print(" 2. Revisar feed personalizado")
print(" 3. Analizar publicaciones existentes")
choice = input("\nElige una opción (1-3): ").strip()
if choice == "1":
hashtag = input("Hashtag a buscar (ej: sorteo): ").strip() or "sorteo"
count = int(input("Número de publicaciones a revisar (default 20): ").strip() or "20")
print(f"\n🚀 Iniciando búsqueda con hashtag: #{hashtag}")
inscriber.run_on_hashtag(hashtag, count)
elif choice == "2":
print("\n📱 Obteniendo feed...")
feed = scraper.get_feed(count=15)
if feed:
print(f"📊 {len(feed)} publicaciones obtenidas del feed")
inscriber.auto_enter(feed)
else:
print("❌ No se pudieron obtener publicaciones del feed")
elif choice == "3":
# Permitir analizar publicaciones manualmente proporcionadas
print("\n📝 Ingresa URLs o captions para analizar (enter vacío para terminar):")
posts = []
while True:
entry = input("> ").strip()
if not entry:
break
posts.append({"caption": entry, "url": entry})
if posts:
print("\n🔍 Analizando publicaciones...")
inscribed = inscriber.auto_enter(posts)
print(f"\n✅ Total inscripciones: {len(inscribed)}")
else:
print("❌ Opción no válida")
# Cerrar scraper
scraper.close()
print("\n" + "=" * 60)
print("¡Gracias por usar Instagram Sorteo Analyzer!")
print("=" * 60)
if __name__ == "__main__":
main()
+98
View File
@@ -0,0 +1,98 @@
"""Módulo para interactuar con Instagram - scraping básico de feeds."""
import requests
from bs4 import BeautifulSoup
from urllib.parse import quote_plus
import time
import random
class InstagramScraper:
"""Clase básica para obtener contenido de Instagram."""
def __init__(self, username=None, password=None):
self.username = username or "usuario_default"
self.password = password or "password_default"
self.session = requests.Session()
self.base_url = "https://www.instagram.com"
def get_feed(self, count=10):
"""Obtener publicaciones del feed (versión básica)."""
# Nota: Instagram bloquea el scraping simple.
# Esta es una estructura que se puede mejorar con headers adecuados
# o usando la API oficial de Instagram.
try:
# Endpoint público para explorar
url = f"{self.base_url}/explore/feed/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
}
response = self.session.get(url, headers=headers, timeout=15)
if response.status_code == 200:
return self._parse_feed(response.text, count)
else:
print(f"Error accediendo a Instagram: {response.status_code}")
return []
except Exception as e:
print(f"Excepción al obtener feed: {e}")
return []
def _parse_feed(self, html, count):
"""Parsear el feed de HTML."""
soup = BeautifulSoup(html, "html.parser")
posts = []
# Buscar artículos o divs de publicaciones
# La estructura de Instagram cambia frecuentemente, por esto es básico
posts_divs = soup.find_all("div", class_="._aagw")[:count]
for div in posts_divs:
try:
# Título/caption aproximado
caption = div.find("span", class_="._aacl _aacp _aacv _aacu")
if caption:
text = caption.get_text(strip=True)
else:
text = ""
# Enlace
link = div.find("a")
href = link.get("href") if link else ""
full_url = f"{self.base_url}{href}" if href else ""
# Imagen
img = div.find("img")
img_url = img.get("src") if img else ""
posts.append({
"url": full_url,
"caption": text,
"image": img_url,
"likes": 0, # Valor por defecto
"comments": 0
})
except Exception:
continue
return posts
def search_hashtag(self, hashtag, count=10):
"""Buscar publicaciones por hashtag."""
try:
url = f"{self.base_url}/explore/tags/{quote_plus(hashtag.lstrip('/'))}/"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36"
}
response = self.session.get(url, headers=headers, timeout=15)
if response.status_code == 200:
return self._parse_feed(response.text, count)
except Exception as e:
print(f"Error buscando hashtag: {e}")
return []
def close(self):
"""Cerrar sesión."""
self.session.close()