/start-11-3.es
Lesson command
$ npx -y skills add minicoohei/ai-agent-camp --agent claude-codeHow it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/start-11-3.es
Context preview
What this command does when you run it.
Lesson command
Command definition
start-11-3.es.mddescription: "Lesson command"
chapter: "courses/aiagent/lesson03-core/module11-github-actions"
duration: "~25 min"
prerequisites: ["start-11-2"]
level: "intermediate"
tags: ["github-actions", "news", "email", "slack", "webhook", "cron"]
nonInteractiveMode: deferred
๐ Lesson 11-3: Flujo de trabajo de obtencion de noticias y distribucion por correo/Slack
๐ Lo que hara en esta sesion
**Leccion 11-3: Obtencion de noticias y distribucion por correo/Slack**!
| Elemento | Contenido | |------|------| | Objetivo | Construir un flujo de trabajo en GitHub Actions que obtiene noticias automaticamente y las distribuye por correo electronico y Slack | | Duracion | ~25 min | | Habilidades utilizadas | GitHub Actions, Python (requests), Slack Webhook, smtplib | | Requisitos previos | Leccion 11-2 completada (comprension de la configuracion de Secrets) |
**Flujo de la sesion:** 1. Creacion del script de obtencion de noticias 2. Implementacion del envio por correo electronico 3. Configuracion de notificaciones via Slack Webhook 4. Creacion del flujo de trabajo de GitHub Actions 5. Configuracion de Secrets y pruebas de funcionamiento
Al final de esta sesion, tendra un pipeline que recopila noticias periodicamente y las distribuye automaticamente por correo electronico y Slack.
> **๐ก Consejo**: Si la respuesta de la IA se detiene a mitad de camino, escriba "por favor continue" o "siga adelante" para reanudar.
---
๐ฏ Verificacion de preparacion
**Configuracion de AskQuestion:**
{
"title": "๐ฏ Verificacion previa a la sesion",
"questions": [{
"id": "readiness",
"prompt": "Esta listo/a?",
"options": [
{"id": "ready", "label": "Listo! Comencemos"},
{"id": "check_prereq", "label": "Verificar requisitos previos"},
{"id": "different_lesson", "label": "Ir a otra leccion"}
]
}]
}(ready โ Ir al Step 1) (check_prereq โ Verificar que la Leccion 11-2 esta completada. Verificar la existencia del directorio `.github/workflows/`) (different_lesson โ Mostrar lista de modulos)
---
๐ Step 1: Creacion del script de obtencion de noticias
{
"title": "๐ Step 1: Script de obtencion de noticias",
"questions": [{
"id": "step_action",
"prompt": "Crearemos un script en Python que obtiene noticias desde feeds RSS o la News API.",
"options": [
{"id": "practice", "label": "Continuar"},
{"id": "review", "label": "Revisar el funcionamiento de RSS/API"},
{"id": "skip", "label": "Omitir"}
]
}]
}**Indicaciones tras la seleccion (ejemplo)**:
Crear `tools/fetch_news.py`:
#!/usr/bin/env python3
"""Script de obtencion de noticias โ Recopila noticias desde feeds RSS"""
import json
import xml.etree.ElementTree as ET
from datetime import datetime
import requests
# URLs de feeds RSS (ejemplo: Hacker News, TechCrunch)
RSS_FEEDS = [
{"name": "Hacker News", "url": "https://hnrss.org/newest?count=5"},
{"name": "TechCrunch", "url": "https://techcrunch.com/feed/"},
]
def fetch_rss(url, max_items=5):
"""Obtener noticias desde un feed RSS"""
resp = requests.get(url, timeout=30)
resp.raise_for_status()
root = ET.fromstring(resp.text)
items = []
for item in root.iter("item"):
title = item.findtext("title", "")
link = item.findtext("link", "")
pub_date = item.findtext("pubDate", "")
items.append({"title": title, "link": link, "pubDate": pub_date})
if len(items) >= max_items:
break
return items
def main():
all_news = []
for feed in RSS_FEEDS:
try:
items = fetch_rss(feed["url"])
all_news.append({"source": feed["name"], "items": items})
except Exception as e:
print(f"[WARN] {feed['name']}: {e}")
# Salida JSON
output = {
"generated_at": datetime.utcnow().isoformat(),
"feeds": all_news
}
with open("output/news_digest.json", "w") as f:
json.dump(output, f, ensure_ascii=False, indent=2)
print(f"Obtencion completada: {sum(len(f['items']) for f in all_news)} noticias")
return output
if __name__ == "__main__":
main()mkdir -p output && python tools/fetch_news.py
**Resultado esperado**: Los datos de noticias se guardan en `output/news_digest.json`.
---
๐ Step 2: Implementacion del envio por correo electronico
{
"title": "๐ Step 2: Envio por correo",
"questions": [{
"id": "step_action",
"prompt": "Agregaremos el proceso de envio de las noticias obtenidas por correo electronico.",
"options": [
{"id": "practice", "label": "Continuar"},
{"id": "review", "label": "Revisar el uso de smtplib"},
{"id": "skip", "label": "Omitir"}
]
}]
}**Indicaciones tras la seleccion (ejemplo)**:
Agregar la funcion de envio a `tools/fetch_news.py`:
import smtplib
from email.mime.text import MIMEText
import os
def send_email(news_data):
"""Enviar resumen de noticias por correo electronico"""
smtp_user = os.environ.get("SMTP_USER", "")
smtp_pass = os.environ.get("SMTP_PASS", "")
to_email = os.environ.get("NOTIFY_EMAIL", smtp_user)
if not smtp_user or not smtp_pass:
print("[SKIP] Envio de correo omitido: credenciales SMTP no configuradas")
return
# Crear cuerpo del correo
body_lines = [f"# Resumen de noticias ({news_data['generated_at'][:10]})\n"]
for feed in news_data["feeds"]:
body_lines.append(f"\n## {feed['source']}")
for item in feed["items"]:
body_lines.append(f"- [{item['title']}]({item['link']})")
body = "\n".join(body_lines)
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = f"Resumen de noticias {news_data['generated_at'][:10]}"
msg["From"] = smtp_user
msg["To"] = to_email
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)Read more
description: "Lesson command" chapter: "courses/aiagent/lesson03-core/module11-github-actions" duration: "~25 min" prerequisites: ["start-11-2"] level: "intermediate" tags: ["github-actions", "news", "email", "slack", "webhook", "cron"] nonInteractiveMode: deferred
๐ Lesson 11-3: Flujo de trabajo de obtencion de noticias y distribucion por correo/Slack
๐ Lo que hara en esta sesion
**Leccion 11-3: Obtencion de noticias y distribucion por correo/Slack**!
| Elemento | Contenido | |------|------| | Objetivo | Construir un flujo de trabajo en GitHub Actions que obtiene noticias automaticamente y las distribuye por correo electronico y Slack | | Duracion | ~25 min | | Habilidades utilizadas | GitHub Actions, Python (requests), Slack Webhook, smtplib | | Requisitos previos | Leccion 11-2 completada (comprension de la configuracion de Secrets) |
**Flujo de la sesion:** 1. Creacion del script de obtencion de noticias 2. Implementacion del envio por correo electronico 3. Configuracion de notificaciones via Slack Webhook 4. Creacion del flujo de trabajo de GitHub Actions 5. Configuracion de Secrets y pruebas de funcionamiento
Al final de esta sesion, tendra un pipeline que recopila noticias periodicamente y las distribuye automaticamente por correo electronico y Slack.
> **๐ก Consejo**: Si la respuesta de la IA se detiene a mitad de camino, escriba "por favor continue" o "siga adelante" para reanudar.
---
๐ฏ Verificacion de preparacion
**Configuracion de AskQuestion:**
{
"title": "๐ฏ Verificacion previa a la sesion",
"questions": [{
"id": "readiness",
"prompt": "Esta listo/a?",
"options": [
{"id": "ready", "label": "Listo! Comencemos"},
{"id": "check_prereq", "label": "Verificar requisitos previos"},
{"id": "different_lesson", "label": "Ir a otra leccion"}
]
}]
}(ready โ Ir al Step 1) (check_prereq โ Verificar que la Leccion 11-2 esta completada. Verificar la existencia del directorio `.github/workflows/`) (different_lesson โ Mostrar lista de modulos)
---
๐ Step 1: Creacion del script de obtencion de noticias
{
"title": "๐ Step 1: Script de obtencion de noticias",
"questions": [{
"id": "step_action",
"prompt": "Crearemos un script en Python que obtiene noticias desde feeds RSS o la News API.",
"options": [
{"id": "practice", "label": "Continuar"},
{"id": "review", "label": "Revisar el funcionamiento de RSS/API"},
{"id": "skip", "label": "Omitir"}
]
}]
}**Indicaciones tras la seleccion (ejemplo)**:
Crear `tools/fetch_news.py`:
#!/usr/bin/env python3
"""Script de obtencion de noticias โ Recopila noticias desde feeds RSS"""
import json
import xml.etree.ElementTree as ET
from datetime import datetime
import requests
# URLs de feeds RSS (ejemplo: Hacker News, TechCrunch)
RSS_FEEDS = [
{"name": "Hacker News", "url": "https://hnrss.org/newest?count=5"},
{"name": "TechCrunch", "url": "https://techcrunch.com/feed/"},
]
def fetch_rss(url, max_items=5):
"""Obtener noticias desde un feed RSS"""
resp = requests.get(url, timeout=30)
resp.raise_for_status()
root = ET.fromstring(resp.text)
items = []
for item in root.iter("item"):
title = item.findtext("title", "")
link = item.findtext("link", "")
pub_date = item.findtext("pubDate", "")
items.append({"title": title, "link": link, "pubDate": pub_date})
if len(items) >= max_items:
break
return items
def main():
all_news = []
for feed in RSS_FEEDS:
try:
items = fetch_rss(feed["url"])
all_news.append({"source": feed["name"], "items": items})
except Exception as e:
print(f"[WARN] {feed['name']}: {e}")
# Salida JSON
output = {
"generated_at": datetime.utcnow().isoformat(),
"feeds": all_news
}
with open("output/news_digest.json", "w") as f:
json.dump(output, f, ensure_ascii=False, indent=2)
print(f"Obtencion completada: {sum(len(f['items']) for f in all_news)} noticias")
return output
if __name__ == "__main__":
main()mkdir -p output && python tools/fetch_news.py
**Resultado esperado**: Los datos de noticias se guardan en `output/news_digest.json`.
---
๐ Step 2: Implementacion del envio por correo electronico
{
"title": "๐ Step 2: Envio por correo",
"questions": [{
"id": "step_action",
"prompt": "Agregaremos el proceso de envio de las noticias obtenidas por correo electronico.",
"options": [
{"id": "practice", "label": "Continuar"},
{"id": "review", "label": "Revisar el uso de smtplib"},
{"id": "skip", "label": "Omitir"}
]
}]
}**Indicaciones tras la seleccion (ejemplo)**:
Agregar la funcion de envio a `tools/fetch_news.py`:
import smtplib
from email.mime.text import MIMEText
import os
def send_email(news_data):
"""Enviar resumen de noticias por correo electronico"""
smtp_user = os.environ.get("SMTP_USER", "")
smtp_pass = os.environ.get("SMTP_PASS", "")
to_email = os.environ.get("NOTIFY_EMAIL", smtp_user)
if not smtp_user or not smtp_pass:
print("[SKIP] Envio de correo omitido: credenciales SMTP no configuradas")
return
# Crear cuerpo del correo
body_lines = [f"# Resumen de noticias ({news_data['generated_at'][:10]})\n"]
for feed in news_data["feeds"]:
body_lines.append(f"\n## {feed['source']}")
for item in feed["items"]:
body_lines.append(f"- [{item['title']}]({item['link']})")
body = "\n".join(body_lines)
msg = MIMEText(body, "plain", "utf-8")
msg["Subject"] = f"Resumen de noticias {news_data['generated_at'][:10]}"
msg["From"] = smtp_user
msg["To"] = to_email
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login(smtp_user, smtp_pass)
server.send_message(msg)AI Agent Training for Non-Engineers - Complete Guide to Claude Code / Cursor / Codex ### โ ๏ธ Before you clone Official repository (maintained by the authors): Running AI agents from this repo grants them shell, file-write, and external-API permissions on your
Other commands on ai-agent-camp.
- /check-setup.en
Top-level alias โ see lesson/check-setup.en.md for the full body.
Open command - /check-setup.es
Alias de nivel superior โ el cuerpo completo estรก en lesson/check-setup.es.md.
Open command - /check-setup
Top-level alias โ see lesson/check-setup.md for the full body.
Open command - /check-security.en
Lesson command
Open command - /check-security.es
Lesson command
Open command - /check-security
Lesson command
Open command

