diff --git a/clase1/edisontana.py b/clase1/edisontana.py new file mode 100644 index 0000000..460857d --- /dev/null +++ b/clase1/edisontana.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# tarea1.py +# +# Copyright 2025 edisontana +# +# Tarea 1. +# Programa que solicita al usuario un número n y visualiza todos los números primos del 1 al n. + +from datetime import datetime + +def esprimo(inumero): + """ + Funcion para saber si un numero es primo. + """ + if inumero <= 1: + False + for i in range( 2, int( inumero ** 0.5) + 1): + if inumero % i == 0: + return False + return True + +def evaluanumeros(n): + listaprimos = [] + icontador = 1 + x = 1 + while True: + if icontador <= n: + if esprimo(x): + listaprimos.append(x) + icontador = icontador + 1 + x = x + 1 + else: + break + print(f"Resultado: Los {n} numeros primos son: ") + print(listaprimos) + + +def visualizafrase(): + texto = "Los buenos equipos incorporan el trabajo en grupo \n a la cultura, creando así los pilares del éxito." + frase = f"{texto:>50}" + texto2 = "Ted Sundquist" + autor = f"{texto2:>50}" + print(frase) + print(autor) + print("") + +def visualizafechaactual(): + print(datetime.now()) + +def muestramenu(opcion): + if opcion == 1: + visualizafrase() + elif opcion == 2: + visualizafechaactual() + else: + quit() + +if __name__ == '__main__': + print("Programa para hallar 'n' numero primos") + print("Realizado por 2025 Edison TANA") + n = int(input("¿Cuantos números primos desea, por favor digite aquí? (numero): ")) + evaluanumeros(n) + print("------------------") + print("Menu interactivo") + print("1. Ver frase motivacional") + print("2. Ver hora actual") + print("3. Salir") + eleccion = int(input("Por favor digite su opcion (1-3): ")) + muestramenu(eleccion) + print("Fin del programa") + + diff --git a/clase1/erika_torres.py b/clase1/erika_torres.py new file mode 100644 index 0000000..61097a8 --- /dev/null +++ b/clase1/erika_torres.py @@ -0,0 +1,53 @@ + + +## 🧪 Ejercicio 1: Números primos + +def es_primo(num): + if num < 2: + return False + for i in range(2, int(num ** 0.5) + 1): + if num % i == 0: + return False + return True + +def mostrar_primos_hasta_n(n): + print(f"Números primos del 1 al {n}:") + for i in range(1, n + 1): + if es_primo(i): + print(i, end=" ") + +# Programa principal +try: + n = int(input("Ingresa un número entero positivo: ")) + if n < 1: + print("Por favor, ingresa un número mayor o igual a 1.") + else: + mostrar_primos_hasta_n(n) +except ValueError: + print("Entrada inválida. Por favor ingresa un número entero.") + +## 🧪 Ejercicio 2: Menú interactivo + +import datetime + +def mostrar_menu(): + print("\n📋 MENÚ PRINCIPAL") + print("1. Mostrar una frase motivacional") + print("2. Mostrar la fecha actual") + print("3. Salir del programa") + +while True: + mostrar_menu() + opcion = input("Elige una opción (1, 2 o 3): ") + + if opcion == "1": + print("\n💬 Frase motivacional:") + print("✨ El éxito es la suma de pequeños esfuerzos repetidos día tras día.") + elif opcion == "2": + fecha_actual = datetime.datetime.now() + print(f"\n📅 Fecha actual: {fecha_actual.strftime('%d/%m/%Y - %H:%M:%S')}") + elif opcion == "3": + print("\n👋 ¡Gracias por usar el programa! Hasta pronto.") + break + else: + print("⚠️ Opción no válida. Por favor, elige 1, 2 o 3.") diff --git a/clase1/jorge_luis_castellanos.py b/clase1/jorge_luis_castellanos.py index 10e390e..6579a30 100644 --- a/clase1/jorge_luis_castellanos.py +++ b/clase1/jorge_luis_castellanos.py @@ -1 +1,54 @@ -print ("Jorge Luis Castellanos") \ No newline at end of file +import datetime + +#Tarea Uno +#____________________________________________________________ +#Ejercicio número 1 +#Determinar si un número ingresado es primo +#____________________________________________________________ + +def numero_primo(numero): + if numero % 2 == 0: + return False + for i in range(3, int(numero ** 0.5) + 1, 2): + if numero % i == 0: + return False + return True + +def numeros_primos(numero): + primos = [] + for i in range(2, numero + 1): + if numero_primo(i): + primos.append(i) + return primos + +#____________________________________________________________ +#Ejercicio número 2 +#Menú interactivo +#____________________________________________________________ + +def menu(): + print("--- Bienvenido al menú interactivo ---") + print("|1| Frase motivacional") + print("|2| Fecha actual") + print("|3| Salir") + opcion = input("Ingrese una opcion: ") + if opcion == "1": + print("🎯 El éxito es la suma de pequeños esfuerzos repetidos día tras día.") + elif opcion == "2": + fecha_actual = print(" 🗓️ La fecha actual es: ", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + else: + print("👋🏼 Fin del programa, Hasta pronto.") + + +if __name__ == "__main__": + + #Ejercicio número 1 + numero = int(input("Ingrese un número mayor a 2: ")) + if numero <= 2: + print("Por favor, ingrese un número mayor a 2.") + else: + primos = numeros_primos(numero) + print(f"Numeros primos hasta {numero}: {primos}") + + #Ejercicio número 2 + menu() \ No newline at end of file diff --git a/clase1/santiago_calvopina.py b/clase1/santiago_calvopina.py new file mode 100644 index 0000000..6be382d --- /dev/null +++ b/clase1/santiago_calvopina.py @@ -0,0 +1,46 @@ +from datetime import datetime + +def verificar_primo(num): + if num <= 1: + return False + for divisor in range(2, num): + if num % divisor == 0: + return False + return True + +def listar_primos(limite): + return [numero for numero in range(2, limite + 1) if verificar_primo(numero)] + +def mostrar_menu(): + while True: + print("\n=== MENÚ PRINCIPAL ===") + print("1. Mostrar una frase motivacional") + print("2. Mostrar la fecha actual") + print("3. Salir del programa") + + opcion = input("Selecciona una opción (1-3): ") + + if opcion == '1': + print("La vida es un viaje, no un destino..") + elif opcion == '2': + ahora = datetime.now() + print("La Fecha y hora actual es:", ahora.strftime("%d/%m/%Y %H:%M:%S")) + elif opcion == '3': + print("¡CHAIIITOO!") + break + else: + print("La Opción no es válida. Ingresa una opción valida.") + +if __name__ == "__main__": + print("Iniciando ...") + try: + numero_usuario = int(input("Ingresa un número positivo mayor que 1: ")) + if numero_usuario > 1: + lista_primos = listar_primos(numero_usuario) + print(f"Los números primos hasta {numero_usuario} son: {lista_primos}") + else: + print("El número debe ser mayor que 1.") + except ValueError: + print("Debe ingresar un número entero válido.") + + mostrar_menu() \ No newline at end of file diff --git a/clase2/edisontana.py b/clase2/edisontana.py new file mode 100644 index 0000000..1f1a2ed --- /dev/null +++ b/clase2/edisontana.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# tarea2.py +# +# Copyright 2025 edisontana +# +# Tarea 2. +# Ejercicio1: Defina un clase Pais con dos metodos +# Ejercicio2: Listas y diccionarios +# Ejercicio3: Trivia con POO + +from datetime import datetime + +class Pais: + + def __init__(self, nombreES, nombreEN, codigoISOA3): + """ + Crea un constructor + """ + self.nombreES = nombreES + self.nombreEN = nombreEN + self.codigoISOA3 = codigoISOA3 + + def verArea(self, pais): + """ + Visuliza el area superficial del pais + """ + # Diccionario de áreas de países (puedes expandir este diccionario) + self.areas = { + 'ecuador': 256370, + 'colombia': 1141748, + 'peru': 1285216, + 'argentina': 2780400, + 'chile': 756102 + } + + strPais = pais.lower() + if strPais in self.areas: + return self.areas[strPais] + else: + return f"No se encontró información para el país {pais}" + + + def verCapital(self, pais): + """ + Muestra la capital del pais + """ + self.capitales = { + 'ecuador': 'Quito', + 'colombia': 'Bogota', + 'peru': 'Lima', + 'argentina': 'Buenos Aires', + 'chile': 'Santiago' + } + strPais = pais.lower() + if strPais in self.capitales: + return self.capitales[strPais] + else: + return f"No se encontró información para el país {pais}" + +# Ejercicio2: Defina un clase Pais con dos metodos + +class Pelicula: + def __init__(self, anio, nombre, genero): + """ + Constructor de la clase Pelicula + + Args: + anio (int): Año de producción + nombre (str): Nombre de la pelicula + genero (str): Género de la pelicula + """ + self.anio = anio + self.nombre = nombre + self.genero = genero + + def __str__(self): + """ + Representación en cadena de la película + """ + return f"{self.nombre} ({self.anio}) - {self.genero}" + +class PeliculasFavoritas: + def __init__(self): + """ + Constructor de la clase PeliculasFavoritas + """ + self.peliculas = [] + + def agregapelicula(self, anio, nombre, genero): + """ + Agrega una nueva película a la lista + + Args: + anio (int): Año de producción + nombre (str): Nombre de la pelicula + genero (str): Género de la pelicula + """ + nuevapelicula = Pelicula(anio, nombre, genero) + self.peliculas.append(nuevapelicula) + print(f"Se agregó {nombre}") + + def buscaporanio(self, anio): + """ + Busca una pelicula por su año de producción + + Args: + anio (int): Año a buscar + + Returns: + Pelicula or str: Pelicula encontrada o mensaje de no encontrado + """ + for pelicula in self.peliculas: + if pelicula.anio == anio: + return pelicula + return f"No se encontró pelicula en el {anio}" + + def muestratodaspeliculas(self): + """ + Muestra todas las peliculas en la lista + """ + if not self.peliculas: + print("El catálogo está vacío.") + return 0 + + print("\n--- Catálogo de Películas ---") + for pelicula in self.peliculas: + print(pelicula) + +def main(): + """ + Función principal para demostrar el catálogo de películas + """ + # Crear instancia del catálogo + favorita1 = PeliculasFavoritas() + + # Agregar películas al catálogo + favorita1.agregapelicula(2010, "Inception", "Ciencia Ficción") + favorita1.agregapelicula(1972, "El Padrino", "Drama") + favorita1.agregapelicula(1999, "Matrix", "Ciencia Ficción") + + # Mostrar películas + favorita1.muestratodaspeliculas() + +# Ejercicio3: Defina un clase Pais con dos metodos +class Pregunta: + + def __init__(self, enunciado, opciones, respuesta): + """ + Constructor de la clase Pregunta + + Args: + enunciado (str): cadena de texto del enunciado + opciones (int): numero de opcion + respuesta (int): respuesta correcta + """ + self.enunciado = enunciado + self.opciones = opciones + self.respuesta = respuesta + + def __str__(self): + """ + Representación en cadena de la pregunta + """ + return f"{self.enunciado}" + + def visualizaopciones(self): + """ + Muestra las opciones de respuesta + """ + print(self.enunciado) + for i, opcion in enumerate(self.opciones, 1): + print(f"{i}. {opcion}") + + def verificarespuesta(self, respuesta_usuario): + """ + Verifica si la respuesta del usuario es correcta + + Args: + respuesta_usuario (int): respuesta seleccionada por el usuario + + Returns: + bool: True si la respuesta es correcta, False en caso contrario + """ + # Ajustar el índice para que coincida con la lista (restar 1) + return respuesta_usuario - 1 == self.respuesta + + def obtienerespuestacorrecta(self): + """ + Devuelve la respuesta correcta + + Returns: + str: Opción correcta + """ + return self.opciones[self.respuesta] + +# Ejemplo de pregunta +def main3(): + # Crear una pregunta + pregunta1 = Pregunta( + "¿Cuál es la capital de Francia?", + ["Londres", "Berlín", "París", "Madrid"], + 2 # París es la respuesta correcta (índice 2) + ) + + # Mostrar la pregunta y sus opciones + pregunta1.visualizaopciones() + + # Solicitar respuesta del usuario + try: + respuestausuario = int(input("^.^ Ingrese el número de su respuesta: ")) + + # Verificar la respuesta + if pregunta1.verificarespuesta(respuestausuario): + print("¡Respuesta correcta!") + else: + print(f"Respuesta incorrecta. La respuesta correcta es: {pregunta1.obtienerespuestacorrecta()}") + + except ValueError: + print("Por favor, ingrese un número válido.") + +if __name__ == '__main__': + + print("Programa de la Tarea 2") + print("Realizado por 2025 Edison TANA") + print("Enunciado 1") + pais1 = Pais('Ecuador','Ecuador','ECU') + pais2 = Pais('Colombia','Colombia','COL') + print(f" >> Pais: {pais1.nombreES}") + print(pais1.verCapital(pais1.nombreES)) + print(f"{pais1.verArea(pais1.nombreES)} km²") + print(f" >> Pais: {pais2.nombreES}") + print(pais2.verCapital(pais2.nombreES)) + print(f"{pais2.verArea(pais2.nombreES)} km²") + print("Enunciado 2") + main() + print("Enunciado 3") + main3() + print("Fin del programa") diff --git a/clase2/erika_torres.py b/clase2/erika_torres.py new file mode 100644 index 0000000..321787d --- /dev/null +++ b/clase2/erika_torres.py @@ -0,0 +1,85 @@ +## 🧪 Ejercicio 1: Objetos + +class Bicicleta: + """ + Clase Bicicleta: + Representa una bicicleta con atributos básicos y métodos para simular su uso. + """ + def __init__(self, marca, tipo, color, velocidad_max): + self.marca = marca + self.tipo = tipo # Montaña, Ruta, Urbana, etc. + self.color = color + self.velocidad_max = velocidad_max + self.velocidad_actual = 0 + + def pedalear(self): + if self.velocidad_actual < self.velocidad_max: + self.velocidad_actual += 5 + print(f"Pedaleando... 🚴 Velocidad actual: {self.velocidad_actual} km/h") + else: + print("🚨 Has alcanzado la velocidad máxima.") + + def frenar(self): + if self.velocidad_actual > 0: + self.velocidad_actual -= 5 + print(f"Frenando... ⚠️ Velocidad actual: {self.velocidad_actual} km/h") + else: + print("La bicicleta ya está detenida.") + +# Crear objeto bicicleta +mi_bici = Bicicleta("Giant", "Montaña", "Roja", 25) + +# Usar métodos +mi_bici.pedalear() +mi_bici.pedalear() +mi_bici.frenar() + +## 🧪 Ejercicio 2: Listas y diccionarios + +# Lista de mis 3 películas favoritas +peliculas = ["El Origen", "Coco", "Intensamente"] + +# Diccionario con información de una de ellas +pelicula_favorita = { + "nombre": "El Origen", + "género": "Ciencia Ficción", + "año": 2010 +} + +# Operaciones +print("🎬 Mis películas favoritas son:", peliculas) +print("🎥 Detalles de la favorita:") +print("Nombre:", pelicula_favorita["nombre"]) +print("Género:", pelicula_favorita["género"]) +print("Año:", pelicula_favorita["año"]) + +## 🧪 Ejercicio 3: Trivia con PO + +class Pregunta: + def __init__(self, enunciado, opciones, respuesta_correcta): + self.enunciado = enunciado + self.opciones = opciones + self.respuesta_correcta = respuesta_correcta + + def mostrar(self): + print("❓", self.enunciado) + for i, opcion in enumerate(self.opciones, 1): + print(f"{i}. {opcion}") + + def responder(self, seleccion): + if self.opciones[seleccion - 1] == self.respuesta_correcta: + print("✅ ¡Correcto!") + else: + print(f"❌ Incorrecto. La respuesta era: {self.respuesta_correcta}") + +# Crear pregunta +pregunta1 = Pregunta( + "¿Cuál es la capital de Ecuador?", + ["Guayaquil", "Quito", "Cuenca"], + "Quito" +) + +# Mostrar y pedir respuesta +pregunta1.mostrar() +numero = int(input("Ingresa el número de tu respuesta: ")) +pregunta1.responder(numero) diff --git a/clase2/jorge_luis_castellanos.py b/clase2/jorge_luis_castellanos.py new file mode 100644 index 0000000..4fcd00d --- /dev/null +++ b/clase2/jorge_luis_castellanos.py @@ -0,0 +1,111 @@ +#Tarea Dos +#____________________________________________________________ +#Ejercicio número 1 +#Ejercicios de tuplas, listas y diccionarios +#____________________________________________________________ + +class automovil: + def __init__(self): + self.marca = "" + self.modelo = "" + self.anio = "" + self.color = "" + + def __init__(self,marca,modelo,anio,color): + self.marca = marca + self.modelo = modelo + self.anio = anio + self.color = color + + def informacion(self): + print(f"Marca: {self.marca} Modelo: {self.modelo} Año: {self.anio} Color: {self.color}") + + def aceleracion(self): + print(f"El auto {self.marca} {self.modelo}, tiene un tiempo de aceleracion de 5 segundos de 0/100 Km") + + def cantidad_pasajeros(self): + print(f"El auto {self.marca} {self.modelo}, tiene la capacidad de 5 pasajeros") + + def sonido(self): + print(f"El auto {self.marca} {self.modelo} posee android car y Apple Car, con Spotify") + + def seguridad(self): + print(f"El auto {self.marca} {self.modelo} posee camaras 360° y graba todo el recorrido del viaje") + +#____________________________________________________________ +#Ejercicio número 2 +#Creación de un juego de trivia +#____________________________________________________________ +class Pregunta: + opciones = [] + def __init__(self,pregunta,respuesta,opciones): + self.pregunta = pregunta + self.opciones = list(opciones) + self.respuesta = respuesta + + def mostrar_pregunta(self): + print(f" {self.pregunta} : ") + indice = 1 + for opcion in self.opciones: + print(f" {indice} {opcion}") + indice += 1 + + def validar_respuesta(self,respuesta_usuario): + if self.respuesta == respuesta_usuario: + return True + else: + return False + + +if __name__ == "__main__": + + #Ejercicio número 1 + print("Caracteristicas de Automovil") + automovil = automovil("Mazda","CX-30","2025","rojo") + automovil.informacion() + automovil.aceleracion() + automovil.cantidad_pasajeros() + automovil.sonido() + automovil.seguridad() + print("\n") + + print("Listas y Diccionarios") + pelicula_1 = {"Nombre":"Lord of the rings : La comunidad del anillo","genero":"acción","Anio":2001} + pelicula_2 = {"Nombre":"Lord of the rings : Las dos torres","genero":"acción","Anio":2002} + pelicula_3 = {"Nombre":"Lord of the rings : El retorno del rey","genero":"drama","Anio":2023} + + Listado = [pelicula_1, pelicula_2, pelicula_3] + + for pelicula in Listado: + print(pelicula) + + pelicula_3["genero"]="accion" + pelicula_3["Anio"] = 2003 + + pelicula_4 = {"Nombre":"El hobbit : Un viaje inesperado","genero":"accion","Anio":2012} + + Listado.append(pelicula_4) + Listado.remove(pelicula_3) + + print("\n") + print("Listado despues de eliminar pelicula 3 y posicionar pelicula 4") + print("\n") + + for pelicula in Listado: + print(pelicula) + + print("\n") + + + #Ejercicio número 2 + print("Trivia") + pregunta = Pregunta("Cuál es la primera pelicula de Lord of the rings", 2, ["EL retorno del rey","La comunindad del anillo","Las dos torres","Un viaje inesperado"]) + + pregunta.mostrar_pregunta() + + respuesta_correcta = int(input("Su respuesta es (ingrese el número correspondiente):")) + + if pregunta.validar_respuesta(respuesta_correcta): + print("Respuesta correcta") + else: + print("Respuesta Incorrecta") \ No newline at end of file diff --git a/clase2/santiago_calvopina.py b/clase2/santiago_calvopina.py new file mode 100644 index 0000000..fc1d3fc --- /dev/null +++ b/clase2/santiago_calvopina.py @@ -0,0 +1,103 @@ +# ////////////////////////////////// Ejercicio 1: Objetos ////////////////////////////////// + +# Objeto: Computadora + +""" Atributos: +- marca: fabricante de la computadora +- sistema_operativo: sistema operativo instalado +- ram: memoria RAM en GB +- almacenamiento: capacidad del disco en GB + +Métodos: +- encender(): imprime que la computadora está encendida +- apagar(): imprime que la computadora se está apagando +- mostrar_info(): muestra detalles de la computadora + +Este objeto fue elegido porque es algo que usamos a diario y es fácil de relacionar con atributos y acciones reales. """ + + + +class Computadora: + def __init__(self, marca, sistema_operativo, ram, almacenamiento): + self.marca = marca + self.sistema_operativo = sistema_operativo + self.ram = ram + self.almacenamiento = almacenamiento + + def encender(self): + print(f"La computadora {self.marca} se está encendiendo...") + + def apagar(self): + print(f"La computadora {self.marca} se está apagando...") + + def mostrar_info(self): + print(f"Marca: {self.marca}") + print(f"Sistema Operativo: {self.sistema_operativo}") + print(f"RAM: {self.ram} GB") + print(f"Almacenamiento: {self.almacenamiento} GB") + +# ////////////////////////////////// Ejercicio 2: Listas y Diccionarios ////////////////////////////////// + +peliculas_favoritas = ["Interestelar", "Coco", "Matrix"] + +info_peliculas = [ + {"titulo": "Interestelar", "genero": "Ciencia Ficción", "anio": 2014}, + {"titulo": "Coco", "genero": "Animación", "anio": 2017}, + {"titulo": "Matrix", "genero": "Acción", "anio": 1999} +] + +print("\n Mis películas favoritas:") +print(peliculas_favoritas) + +print("\n Información de las películas:") +for peli in info_peliculas: + print(peli) + +# Operaciones +print("\n Operaciones con diccionarios:") +info_peliculas[1]["anio"] = 2018 +nueva_pelicula = {"titulo": "Inception", "genero": "Ciencia Ficción", "anio": 2010} +info_peliculas.append(nueva_pelicula) +info_peliculas.pop(0) + +print("Después de las modificaciones:") +for peli in info_peliculas: + print(peli) + +# ////////////////////////////////// Ejercicio 3: Trivia con POO ////////////////////////////////// + +class Pregunta: + def __init__(self, enunciado, opciones, respuesta_correcta): + self.enunciado = enunciado + self.opciones = opciones + self.respuesta_correcta = respuesta_correcta + + def mostrar(self): + print(f"\n {self.enunciado}") + for i, opcion in enumerate(self.opciones, start=1): + print(f"{i}. {opcion}") + + def verificar(self, eleccion_usuario): + return eleccion_usuario == self.respuesta_correcta + +# Pregunta +pregunta = Pregunta( + "¿Cuál es el lenguaje que se ejecuta en el navegador?", + ["Python", "C++", "JavaScript", "Java"], + 3 +) + +pregunta.mostrar() +respuesta_usuario = int(input("Elige la opción correcta (1-4): ")) + +if pregunta.verificar(respuesta_usuario): + print(" ¡Respuesta correcta!") +else: + print(" Respuesta incorrecta.") + +# Impresión clase Computadora +print("\n Objeto Computadora:") +mi_pc = Computadora("Lenovo", "Windows 11", 16, 512) +mi_pc.mostrar_info() +mi_pc.encender() +mi_pc.apagar() \ No newline at end of file diff --git a/clase2/valeria_ramos.py b/clase2/valeria_ramos.py new file mode 100644 index 0000000..754232f --- /dev/null +++ b/clase2/valeria_ramos.py @@ -0,0 +1,112 @@ + +# # Objeto Televisión +# """ +# Estudiante: +# - edad +# - año +# - nota +# Metodos: +# - información +# - aprobar +# """ + +#EJERCICIO 1 OBJETOS +# ¿Qué es un objeto? +# Un objeto es como una cosa o persona en la vida real, pero en programación. Tiene datos que lo describen y cosas que puede hacer. + +# ¿Qué hace el objeto Estudiante? +# Este objeto representa a un estudiante, con su nombre, edad, nota y curso. Además, puede mostrar su información y decir si aprobó o no. + +# ¿Por qué elegí Estudiante? +# Porque es fácil de entender y es algo que todos conocemos. Así puedo practicar cómo guardar información y usarla para hacer cosas, como decir si el estudiante pasó la materia. + +class Estudiante: + def __init__(self, nombre, edad, nota, curso): + self.nombre = nombre + self.edad = edad + self.nota = nota + self.curso = curso + + def informacion (self): + print (f"Nombre:{nombre}") + print (f"Edad:{edad}") + print (f"Nota:{nota}") + print (f"curso:{curso}") + + def aprobar (self): + if self.nota>=7: + print(f"{self.nombre} Felicidades! aprobaste.") + else: + print(f"{self.nombre} No aprobaste." ) + +#se pide los datos +nombre = input ("Ingresa tu nombre:") +edad = int ( input ("Ingresa tu edad:")) +nota = float(input("Ingresa tu nota:")) +curso = input("Ingrea tu curso:") + + +#creación de objeto +est1 = Estudiante(nombre, edad, nota, curso) + +#uso de métodos +est1.informacion() +est1.aprobar() + +# Ejercicio2: LISTAS Y DICCIONARIOS +#lista +pelis_fav = ["voces inocentes", "crepusculo", "Yo antes de ti"] + +print(f"Mi pelis favoritas son: {pelis_fav}") + +#dicionario + +peliculas = [ + {"nombre": "Voces Inocentes","genero": "Drama", "año": 2004 }, + {"nombre":"Crepúsculo", "genero": "Fantasía", "año": 2000}, + {"nombre": "Yo antes de ti", "genero":"Romance", "año":2005} +] + +for peli in peliculas: + print("Nombre:", peli["nombre"]) + print("Género:", peli["genero"]) + print("Año:", peli["año"]) + print("---") + + +#EJERCICIO 3: TRIVIA CON PPO + +class Pregunta: + def __init__(self, enunciado, opciones, respuesta): + self.enunciado = enunciado + self.opciones = opciones + self.respuesta = respuesta + + + def mostrar(self): + print(self.enunciado) + for i, opcion in enumerate(self.opciones, 1): + print(f"{i}. {opcion}") + + def verificar(self, respuesta): + return respuesta == self.respuesta + + + +# Objectos de clase pregunta +p1 = Pregunta("¿Cuál es la capital de Francia?", ["Madrid", "París", "Londres"], 2) +p2 = Pregunta("¿Qué planeta es conocido como el Planeta Rojo?", ["Venus", "Marte", "Júpiter"], 2) +p3 = Pregunta("¿Cuántos continentes hay en el mundo?", ["5", "6", "7", "8"], 3) + +# Los ponemos en una lista +lista_preguntas = [p1, p2, p3] + +# Recorremos la lista para jugar la trivia +for pregunta in lista_preguntas: + pregunta.mostrar() + respuesta = int(input("Escribe el número de la opción correcta: ")) + if pregunta.verificar(respuesta): + print("¡Correcto!\n") + else: + print(f"Incorrecto. La respuesta correcta era la opción {pregunta.respuesta}\n") + \ No newline at end of file diff --git a/clase3/Lizeth_Albacura/Chisteapi/Chiste.py b/clase3/Lizeth_Albacura/Chisteapi/Chiste.py new file mode 100644 index 0000000..51c3244 --- /dev/null +++ b/clase3/Lizeth_Albacura/Chisteapi/Chiste.py @@ -0,0 +1,22 @@ +from flask import Flask, render_template +import requests + +app = Flask(__name__) + +def obtener_chiste(): + url = "https://official-joke-api.appspot.com/jokes/random" + try: + response = requests.get(url) + response.raise_for_status() + data = response.json() + return data.get("setup"), data.get("punchline") + except requests.RequestException: + return "Error al obtener el chiste.", "" + +@app.route("/") +def index(): + setup, punchline = obtener_chiste() + return render_template("index.html", setup=setup, punchline=punchline) + +if __name__ == "__main__": + app.run(debug=True) diff --git a/clase3/Lizeth_Albacura/Chisteapi/templates/index.html b/clase3/Lizeth_Albacura/Chisteapi/templates/index.html new file mode 100644 index 0000000..e578b36 --- /dev/null +++ b/clase3/Lizeth_Albacura/Chisteapi/templates/index.html @@ -0,0 +1,36 @@ + + + + + Chiste del Día + + + +
+

😂 Chiste para alegrarte el día

+

{{ setup }}

+

{{ punchline }}

+
+ 🔄 Siguiente chiste +
+ + \ No newline at end of file diff --git a/clase3/Lizeth_Albacura/Pokeapi/Pokeapi.py b/clase3/Lizeth_Albacura/Pokeapi/Pokeapi.py new file mode 100644 index 0000000..7c6e09f --- /dev/null +++ b/clase3/Lizeth_Albacura/Pokeapi/Pokeapi.py @@ -0,0 +1,61 @@ +from flask import Flask, render_template, request +import requests +import math + +app = Flask(__name__) + +POKEMONS_PER_PAGE = 9 + +@app.route('/', methods=['GET']) +def index(): + search_query = request.args.get('search', '').lower() + page = int(request.args.get('page', 1)) + + offset = (page - 1) * POKEMONS_PER_PAGE + url = f'https://pokeapi.co/api/v2/pokemon?limit={POKEMONS_PER_PAGE}&offset={offset}' + response = requests.get(url) + + if response.status_code != 200: + return "Error al obtener los Pokémon" + + results = response.json() + all_pokemon = [] + + for poke in results['results']: + poke_data = requests.get(poke['url']).json() + name = poke_data['name'] + + if search_query in name: + held_items = [item['item']['name'] for item in poke_data['held_items']] if poke_data['held_items'] else ['None'] + species_name = poke_data['species']['name'] if 'species' in poke_data else 'Unknown' + + pokemon = { + 'name': name.upper(), + 'front_sprite': poke_data['sprites']['front_default'], + 'back_sprite': poke_data['sprites']['back_default'], + 'front_image': poke_data['sprites']['other']['official-artwork']['front_default'], + 'back_image': poke_data['sprites']['other']['official-artwork'].get('back_default', None), + 'height': poke_data['height'], + 'weight': poke_data['weight'], + 'moves': [move['move']['name'] for move in poke_data['moves']] if poke_data['moves'] else ['None'], + 'abilities': [a['ability']['name'] for a in poke_data['abilities']], + 'base_experience': poke_data['base_experience'], + 'order': poke_data['order'], + 'held_items': held_items, + 'types': [t['type']['name'] for t in poke_data['types']], + 'stats': {s['stat']['name']: s['base_stat'] for s in poke_data['stats']}, + 'species': species_name + } + all_pokemon.append(pokemon) + + total = 100 + total_pages = math.ceil(total / POKEMONS_PER_PAGE) + + return render_template('index.html', + pokemon_list=all_pokemon, + search_query=search_query, + page=page, + total_pages=total_pages) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/clase3/Lizeth_Albacura/Pokeapi/templates/index.html b/clase3/Lizeth_Albacura/Pokeapi/templates/index.html new file mode 100644 index 0000000..6a851e3 --- /dev/null +++ b/clase3/Lizeth_Albacura/Pokeapi/templates/index.html @@ -0,0 +1,96 @@ + + + + + Pokédex + + + + +
+

Pokédex

+ +
+ + +
+ +
+ {% for pokemon in pokemon_list %} +
+
+
+
{{ pokemon.name }}
+ + {% if pokemon.front_sprite %} + Frente + {% endif %} + {% if pokemon.back_sprite %} + Espalda + {% endif %} +
+ {% if pokemon.front_image %} + Arte frontal + {% endif %} + {% if pokemon.back_image %} + Arte trasero + {% endif %} + +
    +
  • Altura: {{ pokemon.height }}
  • +
  • Peso: {{ pokemon.weight }}
  • +
  • Especie: {{ pokemon.species }}

    +
  • Tipos: {{ pokemon.types | join(', ') }}

    +
  • + Forma estándar: {{ 'Sí' if pokemon.is_default else 'No' }} +
  • Experiencia Base: {{ pokemon.base_experience }}
  • +
  • Orden:{{ pokemon.order}}
  • +
  • Habilidades: {{ pokemon.abilities | join(', ') }}
  • +
  • Ítems: {{ pokemon.held_items | join(', ') }}
  • +
  • Movimientos: {{ pokemon.moves[:5] | join(', ') }}{% if pokemon.moves|length > 5 %}, ...{% endif %}
  • + +
  • + Stats: +
      + {% for stat, value in pokemon.stats.items() %} +
    • {{ stat }}: {{ value }}
    • + {% endfor %} +
    +
  • +
+
+
+
+ {% endfor %} +
+ +
+ {% if page > 1 %} + ← Anterior + {% endif %} + Página {{ page }} de {{ total_pages }} + {% if page < total_pages %} + Siguiente → + {% endif %} +
+
+ + + + \ No newline at end of file diff --git a/clase3/flask_chistes/erika_torres.py b/clase3/flask_chistes/erika_torres.py new file mode 100644 index 0000000..20c0fe7 --- /dev/null +++ b/clase3/flask_chistes/erika_torres.py @@ -0,0 +1,61 @@ +import requests + +URL = "https://fakestoreapi.com" + +def deserialize_json(json_data): + """ + Deserialize JSON data into a Python object. + """ + try: + return json_data.json() + except ValueError as e: + print(f"Error deserializing JSON: {e}") + return None + +def serialize_json(data): + """ + Serialize a Python object into JSON format. + """ + try: + return json.dumps(data, indent=2) + except TypeError as e: + print(f"Error serializing to JSON: {e}") + return None + +def get_products(): + url = f"{URL}/products" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return None + +def get_product_by_id(product_id): + url = f"{URL}/products/{product_id}" + response = requests.get(url) + if response.status_code == 200: + return response.json() + else: + return None + +if __name__ == "__main__": + products = get_products() + for product in products: + print(f"Product ID: {product['id']}") + print(f"Title: {product['title']}") + print(f"Price: {product['price']}") + print(f"Description: {product['description']}") + print(f"Imagen: {product['image']}") + print("-" * 20) + + product = get_product_by_id(15) + print(f"Product ID: {product['id']}") + print(f"Title: {product['title']}") + print(f"Price: {product['price']}") + print(f"Description: {product['description']}") + print(f"Imagen: {product['image']}") + print("-" * 20) + + objserialize = serialize_json(products) + print(f"Serialized JSON: {objserialize}") + \ No newline at end of file diff --git a/clase3/jorge_luis_castellanos/jokes/app.py b/clase3/jorge_luis_castellanos/jokes/app.py new file mode 100644 index 0000000..e81e3a6 --- /dev/null +++ b/clase3/jorge_luis_castellanos/jokes/app.py @@ -0,0 +1,27 @@ +import requests +from flask import Flask, render_template + +app = Flask(__name__) + +@app.route('/') +def chiste(): + chiste = {} + error = None + + try: + response = requests.get("https://official-joke-api.appspot.com/jokes/random") + if response.status_code == 200: + chiste = response.json() + else: + error = "No se pudo obtener un chiste. Intenta de nuevo." + except Exception as e: + error = f"Error al conectarse a la API: {e}" + + return render_template("index.html", chiste=chiste, error=error) + +@app.route('/salir') +def salir(): + return "

👋 ¡Gracias por reír con nosotros! Hasta la próxima.

" + +if __name__ == '__main__': + app.run(debug=True) diff --git a/clase3/jorge_luis_castellanos/jokes/templates/index.html b/clase3/jorge_luis_castellanos/jokes/templates/index.html new file mode 100644 index 0000000..ae97246 --- /dev/null +++ b/clase3/jorge_luis_castellanos/jokes/templates/index.html @@ -0,0 +1,64 @@ + + + + + + 😂 Chiste del Día + + + + + + + + + + + +
+
+
+
+
😂
+

Chiste del Día

+ + {% if error %} +
{{ error }}
+ {% else %} +

{{ chiste.setup }}

+

{{ chiste.punchline }}

+ {% endif %} + + +
+
+
+
+ + + + \ No newline at end of file diff --git a/clase3/jorge_luis_castellanos/pokemon/app.py b/clase3/jorge_luis_castellanos/pokemon/app.py new file mode 100644 index 0000000..94dbb0f --- /dev/null +++ b/clase3/jorge_luis_castellanos/pokemon/app.py @@ -0,0 +1,54 @@ +import math + +import requests +from flask import Flask, render_template, request + +app = Flask(__name__) + +@app.route('/') +def index(): + page = int(request.args.get('page', 1)) + search = request.args.get('search', '').lower() + per_page = 9 + total_pokemons = 100 + offset = (page - 1) * per_page + pokemons = [] + + if search: + data = get_pokemon(search) + if data: + pokemons.append(data) + total_pages = 1 + else: + url = f'https://pokeapi.co/api/v2/pokemon?offset={offset}&limit={per_page}' + response = requests.get(url) + results = response.json().get('results', []) + for item in results: + poke_data = get_pokemon(item['name']) + if poke_data: + pokemons.append(poke_data) + total_pages = math.ceil(total_pokemons / per_page) + + return render_template('index.html', + pokemon_list=pokemons, + search_query=search, + page=page, + total_pages=total_pages) + +def get_pokemon(name): + url = f'https://pokeapi.co/api/v2/pokemon/{name}' + response = requests.get(url) + if response.status_code == 200: + data = response.json() + return { + 'name': data['name'].capitalize(), + 'image_front': data['sprites']['front_default'], + 'image_back': data['sprites']['back_default'], + 'height': data['height'], + 'weight': data['weight'], + 'abilities': [a['ability']['name'] for a in data['abilities']] + } + return None + +if __name__ == '__main__': + app.run(debug=True) diff --git a/clase3/jorge_luis_castellanos/pokemon/templates/index.html b/clase3/jorge_luis_castellanos/pokemon/templates/index.html new file mode 100644 index 0000000..a9d1ea8 --- /dev/null +++ b/clase3/jorge_luis_castellanos/pokemon/templates/index.html @@ -0,0 +1,59 @@ + + + + + + + Pokédex Flask + + + +
+

POKEAPI

+ +
+
+ + +
+
+ + {% if pokemon_list %} +
+ {% for pokemon in pokemon_list %} +
+
+
+ Frente + Atrás +
+
+
{{ pokemon.name }}
+

+ Altura: {{ pokemon.height }}
+ Peso: {{ pokemon.weight }}
+ Habilidades: {{ pokemon.abilities | join(', ') }} +

+
+
+
+ {% endfor %} +
+ + + + + {% else %} +

No se encontraron Pokémon.

+ {% endif %} +
+ + diff --git a/clase3/valeria_ramos/app.py b/clase3/valeria_ramos/app.py new file mode 100644 index 0000000..04e28d6 --- /dev/null +++ b/clase3/valeria_ramos/app.py @@ -0,0 +1,18 @@ +from flask import Flask, render_template +import requests + +app = Flask(__name__) + +@app.route('/') +def mostrar_chiste(): + url = "https://official-joke-api.appspot.com/jokes/random" + try: + response = requests.get(url) + response.raise_for_status() + data = response.json() + return render_template('index.html', setup=data['setup'], punchline=data['punchline']) + except Exception as e: + return f"Ocurrió un error: {e}" + +if __name__ == '__main__': + app.run(debug=True) diff --git a/clase3/valeria_ramos/templates/index.html b/clase3/valeria_ramos/templates/index.html new file mode 100644 index 0000000..f82137e --- /dev/null +++ b/clase3/valeria_ramos/templates/index.html @@ -0,0 +1,19 @@ + + + + Chiste Random + + + +
+

😂 Chiste Random

+
+

Chiste: {{ setup }}

+

Respuesta: {{ punchline }}

+
+ +
+
+
+ + diff --git a/clase3/valeria_ramos/valeria_ramos.py b/clase3/valeria_ramos/valeria_ramos.py new file mode 100644 index 0000000..3bcabb1 --- /dev/null +++ b/clase3/valeria_ramos/valeria_ramos.py @@ -0,0 +1,41 @@ +import requests + + +limit = 9 +offset = 0 + +while True: + url = f"https://pokeapi.co/api/v2/pokemon?limit={limit}&offset={offset}" + response = requests.get (url) + + if response.status_code == 200: + data = response.json() + print (f"Mostrando Pokemmón {offset+1} a {offset+limit}") + for poke in data ['results']: + print ("-", poke['name']) + else: + print ("Error al obtener datos.") + break + option = input ("Ver más.. (s/n):") + if option.lower() == 's': + offset += limit + else: + break +nombre = input ("\n Ingresa el nombre del Pokemón para ver sus datos:") + +url_pokemon = f"https://pokeapi.co/api/v2/pokemon/{nombre.lower()}" +resp_poke = requests.get(url_pokemon) + +if resp_poke.status_code == 200: + datos = resp_poke.json() + print("\nDatos del Pokémon:") + print("ID:", datos['id']) + print("Nombre:", datos['name']) + tipos = [t['type']['name'] for t in datos['types']] + print("Tipos:", tipos) + print("Altura:", datos['height']) + print("Imagen URL:", datos['sprites']['front_default']) + print("Sprite:", datos['sprites']['front_default']) +else: + print("Pokémon no encontrado.") + \ No newline at end of file diff --git a/entorno/Scripts/dotenv.exe b/entorno/Scripts/dotenv.exe new file mode 100644 index 0000000..793faec Binary files /dev/null and b/entorno/Scripts/dotenv.exe differ diff --git a/entorno/Scripts/pyrsa-decrypt.exe b/entorno/Scripts/pyrsa-decrypt.exe new file mode 100644 index 0000000..340d43f Binary files /dev/null and b/entorno/Scripts/pyrsa-decrypt.exe differ diff --git a/entorno/Scripts/pyrsa-encrypt.exe b/entorno/Scripts/pyrsa-encrypt.exe new file mode 100644 index 0000000..3e6ed2c Binary files /dev/null and b/entorno/Scripts/pyrsa-encrypt.exe differ diff --git a/entorno/Scripts/pyrsa-keygen.exe b/entorno/Scripts/pyrsa-keygen.exe new file mode 100644 index 0000000..bb4eacb Binary files /dev/null and b/entorno/Scripts/pyrsa-keygen.exe differ diff --git a/entorno/Scripts/pyrsa-priv2pub.exe b/entorno/Scripts/pyrsa-priv2pub.exe new file mode 100644 index 0000000..3c36e3a Binary files /dev/null and b/entorno/Scripts/pyrsa-priv2pub.exe differ diff --git a/entorno/Scripts/pyrsa-sign.exe b/entorno/Scripts/pyrsa-sign.exe new file mode 100644 index 0000000..61596ed Binary files /dev/null and b/entorno/Scripts/pyrsa-sign.exe differ diff --git a/entorno/Scripts/pyrsa-verify.exe b/entorno/Scripts/pyrsa-verify.exe new file mode 100644 index 0000000..601e2fc Binary files /dev/null and b/entorno/Scripts/pyrsa-verify.exe differ diff --git a/entorno/Scripts/tqdm.exe b/entorno/Scripts/tqdm.exe new file mode 100644 index 0000000..c743393 Binary files /dev/null and b/entorno/Scripts/tqdm.exe differ diff --git a/proyecto/Wendy_Moreno/Wendy_Moreno.py b/proyecto/Wendy_Moreno/Wendy_Moreno.py new file mode 100644 index 0000000..1b510fa --- /dev/null +++ b/proyecto/Wendy_Moreno/Wendy_Moreno.py @@ -0,0 +1,135 @@ +import os +import smtplib +from email.message import EmailMessage + +import openpyxl +from telebot import TeleBot, types + +# Configuración +TOKEN_BOT = '8130338428:AAFepXJ--dhup-dbLMpp_2ufU_kSwo48mJ8' +bot = TeleBot(TOKEN_BOT) + +SMTP_SERVER = 'smtp.gmail.com' +SMTP_PORT = 587 +SMTP_USER = 'wendymorenoc2024@gmail.com' +SMTP_PASSWORD = 'qwwcjnppcclzzrwh' +DESTINATARIO = 'wendymorenoc2024@gmail.com' + +EXCEL_FILE = 'mensajes_asesor.xlsx' + +# Crear archivo Excel si no existe +def crear_archivo_excel(): + if not os.path.exists(EXCEL_FILE): + wb = openpyxl.Workbook() + ws = wb.active + ws.title = "Mensajes" + ws.append(["Cédula/RUC", "Nombre", "Teléfono", "Mensaje"]) + wb.save(EXCEL_FILE) + +crear_archivo_excel() + +# Estado de usuarios +estado_usuario = {} +datos_usuario = {} + +@bot.message_handler(commands=['start']) +def start(message): + texto = "👋 *Bienvenido a Tributa Bien*\n\n¿En qué te puedo ayudar hoy?" + opciones = [ + "1️⃣ Apertura de RUC", + "2️⃣ Declaración de IVA", + "3️⃣ Declaración de RENTA", + "4️⃣ Patente Municipal Quito", + "5️⃣ Contactar a un Asesor" + ] + markup = types.ReplyKeyboardMarkup(resize_keyboard=True) + for opcion in opciones: + markup.add(opcion) + bot.send_message(message.chat.id, texto, parse_mode="Markdown", reply_markup=markup) + +@bot.message_handler(func=lambda msg: True) +def manejar_mensajes(message): + chat_id = message.chat.id + texto = message.text + + if chat_id in estado_usuario: + paso = estado_usuario[chat_id] + if paso == "cedula": + datos_usuario[chat_id]["cedula"] = texto + estado_usuario[chat_id] = "nombre" + bot.send_message(chat_id, "📛 Escribe tu *nombre completo*:", parse_mode="Markdown") + elif paso == "nombre": + datos_usuario[chat_id]["nombre"] = texto + estado_usuario[chat_id] = "telefono" + bot.send_message(chat_id, "📱 Escribe tu *número de teléfono*:", parse_mode="Markdown") + elif paso == "telefono": + datos_usuario[chat_id]["telefono"] = texto + estado_usuario[chat_id] = "mensaje" + bot.send_message(chat_id, "📝 Escribe tu *mensaje* de consulta:", parse_mode="Markdown") + elif paso == "mensaje": + datos_usuario[chat_id]["mensaje"] = texto + guardar_excel(datos_usuario[chat_id]) + enviar_email(datos_usuario[chat_id]) + bot.send_message(chat_id, "✅ ¡Mensaje enviado correctamente en breve un Asesor se comunicará contigo!") + del estado_usuario[chat_id] + del datos_usuario[chat_id] + return + + if texto.startswith("1️⃣"): + bot.send_message(chat_id, "📌 *Apertura de RUC*\n\nEn línea:\n- Firma electrónica\n- Cédula vigente\n- Factura de luz y agua\n- Correo electrónico\n- Monto de ventas\n- Actividad económica\n💵 Valor: 10 USD\n\nPresencial:\n- Autorización física firmada\n- Cédula\n- Dirección\n💵 Valor: 20 USD", parse_mode="Markdown") + elif texto.startswith("2️⃣"): + bot.send_message(chat_id, "📄 *Declaración de IVA*\n\n- Usuario y clave del SRI\n- Facturas de compras y ventas\n💵 Valor: según facturación", parse_mode="Markdown") + elif texto.startswith("3️⃣"): + bot.send_message(chat_id, "💰 *Declaración de RENTA*\n\n- Usuario y clave del SRI\n- Régimen y facturación\n💵 Valor: 20 USD", parse_mode="Markdown") + elif texto.startswith("4️⃣"): + bot.send_message(chat_id, "🏢 *Patente Municipal Quito*\n\n- Usuario y clave\n- Firma electrónica si aplica\n💵 Valor: 5 a 10 USD", parse_mode="Markdown") + elif texto.startswith("5️⃣"): + bot.send_message(chat_id, "📞 *Contactar a un Asesor*\n\nPor favor ingresa tu *cédula o RUC* para iniciar:", parse_mode="Markdown") + estado_usuario[chat_id] = "cedula" + datos_usuario[chat_id] = {} + else: + bot.send_message(chat_id, "❗ Usa el menú para elegir una opción válida.") + +def guardar_excel(datos): + try: + crear_archivo_excel() + wb = openpyxl.load_workbook(EXCEL_FILE) + ws = wb.active + ws.append([ + datos['cedula'], + datos['nombre'], + datos['telefono'], + datos['mensaje'] + ]) + wb.save(EXCEL_FILE) + print("✅ Mensaje guardado en Excel.") + except Exception as e: + print(f"❌ Error al guardar en Excel: {e}") + +def enviar_email(datos): + try: + msg = EmailMessage() + msg['Subject'] = f"Nuevo mensaje de {datos['nombre']} - {datos['cedula']}" + msg['From'] = SMTP_USER + msg['To'] = DESTINATARIO + + cuerpo = ( + f"Cédula o RUC: {datos['cedula']}\n" + f"Nombre: {datos['nombre']}\n" + f"Teléfono: {datos['telefono']}\n" + f"Mensaje:\n{datos['mensaje']}\n\n" + f"✅ Autorización para uso de datos otorgada por el usuario." + ) + msg.set_content(cuerpo) + + with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server: + server.starttls() + server.login(SMTP_USER, SMTP_PASSWORD) + server.send_message(msg) + print("📧 Correo enviado con éxito, en breve el Asesor se pondrá en contacto contigo.") + except Exception as e: + print(f"❌ Error al enviar correo: {e}") + +if __name__ == '__main__': + print("🤖 Bot ejecutándose...") + bot.infinity_polling() diff --git a/proyecto/Wendy_Moreno/mensajes.csv b/proyecto/Wendy_Moreno/mensajes.csv new file mode 100644 index 0000000..691869c --- /dev/null +++ b/proyecto/Wendy_Moreno/mensajes.csv @@ -0,0 +1,2 @@ +Cedula,Nombre,Telefono,Mensaje +1714653472,Wendy Moreno,0983894502,Quiero mas informacion diff --git a/proyecto/Wendy_Moreno/mensajes_asesor.xlsx b/proyecto/Wendy_Moreno/mensajes_asesor.xlsx new file mode 100644 index 0000000..9c16c8d Binary files /dev/null and b/proyecto/Wendy_Moreno/mensajes_asesor.xlsx differ diff --git a/proyecto/edisontana/README.md b/proyecto/edisontana/README.md new file mode 100644 index 0000000..6a953f4 --- /dev/null +++ b/proyecto/edisontana/README.md @@ -0,0 +1,20 @@ + +--- + +# Proyecto del bot en telegram + +Por Edison TANA +Archivo: mitelegrambotTopograf.py + +## Licencia + +[![CC BY-SA 4.0][cc-by-sa-shield]][cc-by-sa] + +Este trabajo está licenciado bajo +[Creative Commons Attribution-ShareAlike 4.0 International License][cc-by-sa] y GNU GPL v3 + +[![CC BY-SA 4.0][cc-by-sa-image]][cc-by-sa] + +[cc-by-sa]: http://creativecommons.org/licenses/by-sa/4.0/ +[cc-by-sa-image]: https://licensebuttons.net/l/by-sa/4.0/88x31.png +[cc-by-sa-shield]: https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg \ No newline at end of file diff --git a/proyecto/edisontana/proyectobot/mitelegrambotTopograf.py b/proyecto/edisontana/proyectobot/mitelegrambotTopograf.py new file mode 100644 index 0000000..e6ecf94 --- /dev/null +++ b/proyecto/edisontana/proyectobot/mitelegrambotTopograf.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# mitelegrambotTopograf.py +# +# Copyright 2025 Edison TANA +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# Desarrollado en GNU_Linux + + +from telegram import Update, ReplyKeyboardMarkup +from telegram.ext import ( + ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, + filters, ConversationHandler +) +from mailjet_rest import Client +import requests +import os +import re +import sqlite3 +import logging +from datetime import datetime +from dotenv import load_dotenv + +load_dotenv() + +BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") +api_key = os.getenv('MAILJET_API_KEY') +api_secret = os.getenv('MAILJET_SECRET_KEY') + +correoremite = os.getenv('EMAIL_REMITENTE') +correodestino = os.getenv('EMAIL_DESTINATARIO') + + +MAIN_MENU, FIRST_MENU, SECOND_MENU, THIRD_MENU = range(4) + +SERVICIOS = { + "1. 🗺️ Modelos de terreno en 3D": 100, + "2. 📏 Nivelaciones y replanteos": 12, + "3. 🌎 Levantamientos topograficos": 180, + "4. 👨 Asesoría topografica": 60 +} + +conn = sqlite3.connect("mibase.db", check_same_thread=False) +cursor = conn.cursor() + +cursor.execute(""" +CREATE TABLE IF NOT EXISTS chat_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + username TEXT, + servicio TEXT, + message_id INTEGER, + timestamp TEXT +) +""") +conn.commit() + +def guardar_interaccion(update: Update): + user = update.effective_user + username = user.username or "Sin username" + message_id = update.message.message_id + servicio = update.message.text + timestamp = datetime.now().isoformat() + + cursor.execute(""" + INSERT INTO chat_data (user_id, message_id, username, servicio, timestamp) + VALUES (?, ?, ?, ?, ?) + """, (user.id, username, servicio, message_id, timestamp)) + conn.commit() + +# Extraer correo desde un texto +def extraer_email(texto): + match = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', texto) + return match.group(0) if match else None + +def enviarcorreos(context, correodestino, asunto, contenido): + + mailjet = Client(auth=(api_key, api_secret), version='v3.1') + data = { + 'Messages': [ + { + "From": { + "Email": correoremite, # Remove $ and use variable directly + "Name": "Info EuatorTopografia" + }, + "To": [ + { + "Email": correodestino, # Remove $ and use variable directly + "Name": "Cliente de equatortopografia " + } + ], + "Subject": asunto, + "TextPart": "Saludos desde Topografia!", + "HTMLPart": f""" +

Estimado usuario, bienvenido a EquatorTopografia!


Un gusto conectar con usted! +
+

Usted ha elegido el servicio de:

+
    +
  • +

    {context.user_data['servicio']}

    +
  • +
  • Su detalle es {context.user_data['datos']}
  • +
  • Su costo es ${SERVICIOS[context.user_data['servicio']]}
  • +
+
+ Gracias por escribirnos, hasta pronto. +
+

Más servicios

+
+
    +
  1. Modelos de terreno en 3D
  2. +
  3. Levantamientos topograficos
  4. +
  5. +

    Nivelaciones y replanteos con precision topografica

    +
  6. +
  7. +

    Georeferenciaciones

    +
  8. +
  9. +

    Digitalización topografica

    +
  10. +
+
+
+

Contacta por telegram con nuestro asesor en t.me/leosAcadUIO

+ """ + } + ] + } + + result = mailjet.send.create(data=data) + print(result.status_code) + print('Ok') + + +# Inicio del bot +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): + mensaje = ( + """ + Hola. Buen día \n 🌟 Bienvenido/a a AsistenteBot! (@academicobot) 🌟.\n + ______________________________________ + < Te ayudamos en soporte y asesoría de > + ----------------------------------------------------------------------- + + A continuacion elige un ítem de la lista: + """ + ) + keyboard = [[opcion] for opcion in SERVICIOS.keys()] + await update.message.reply_text( + mensaje, + reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True), + parse_mode="Markdown" + ) + guardar_interaccion(update) + return MAIN_MENU + +# Selección del servicio +async def seleccionar_servicio(update: Update, context: ContextTypes.DEFAULT_TYPE): + servicio = update.message.text.strip() + if servicio in SERVICIOS: + context.user_data["servicio"] = servicio + await update.message.reply_text( + f"📝 Has seleccionado *{servicio}*.\n\nPor favor, digita su:\n1. Nombre completo\n2. Correo electrónico\n3. Una breve detalle de su caso:", + parse_mode="Markdown" + ) + guardar_interaccion(update) + return FIRST_MENU + else: + await update.message.reply_text("Opción inválida. Selecciona un servicio del menú.") + return await start(update, context) + + +# Recolección de datos +async def recibir_datos(update: Update, context: ContextTypes.DEFAULT_TYPE): + texto = update.message.text.strip() + email = extraer_email(texto) + if not email: + await update.message.reply_text( + "Se encontró un error.\n De nuevo ingrese su informacion de acuerdo a este formato:\n\n`Nombre - correo@ejemplo.com - detalle`", + parse_mode="Markdown" + ) + guardar_interaccion(update) + return FIRST_MENU + + context.user_data["datos"] = texto + keyboard = [["Sí", "No"]] + await update.message.reply_text( + " Autorizo a EquatorTopografia a verificar la autenticidad de la información proporcionada y utilizarla para fines informativos. Entiendo que mis datos serán tratados conforme a la Ley de Protección de Datos Personales de EC.: \n SI / NO", + reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True) + ) + guardar_interaccion(update) + return SECOND_MENU + +async def autorizacion(update: Update, context: ContextTypes.DEFAULT_TYPE): + respuesta = update.message.text.lower() + # Fix: Remove redundant 'in respuesta' checks + if "si" in respuesta or "sí" in respuesta: + servicio = context.user_data["servicio"] + precio = SERVICIOS[servicio] + keyboard = [["✅ Confirmar", "❌ Cancelar"]] + await update.message.reply_text( + f"El *{servicio}* tiene un costo de *${precio}*.\n\n¿Deseas confirmar el pedido?\n Confirmar / Cancelar", + reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True), + parse_mode="Markdown" + ) + guardar_interaccion(update) + return THIRD_MENU + elif "no" in respuesta: + await update.message.reply_text("No podemos continuar sin tu autorización. Escribe /start para comenzar de nuevo.") + return ConversationHandler.END + else: + await update.message.reply_text("Respuesta inválida. Selecciona *Sí* o *No* desde el menú.") + return SECOND_MENU + +async def confirmar(update: Update, context: ContextTypes.DEFAULT_TYPE): + texto = update.message.text.lower() + guardar_interaccion(update) + # A + if "confirmar" in texto: + servicio = context.user_data["servicio"] + detalle = context.user_data["datos"] + precio = SERVICIOS[servicio] + correo = extraer_email(detalle) + correodestinos = correo + + mensaje = ( + f"*Gracias por tu pedido!*\n\n" + f"*Servicio:* {servicio}\n" + f"*Detalle:* {detalle}\n" + f"*Precio:* ${precio}\n\n" + "Nos pondremos en contacto contigo pronto. 📧" + ) + + # Optional: Add logging instead of print statements + logging.info(f"Email: {detalle}, Precio: {precio}, Correo: {correo}") + logging.info(f"Mensaje: {mensaje}") + + if correo: + try: + enviado = enviarcorreos(context, correodestinos, ":) Info sobre cotizacion para "+correo, mensaje) + await update.message.reply_text("Correo enviado exitosamente.") + except Exception as e: + logging.error(f"Error enviando correo: {e}") + await update.message.reply_text("Hubo un problema al enviar el correo.") + else: + await update.message.reply_text("Correo NO enviado") + + await update.message.reply_text("¿Requieres cotizarotro servicio? Digita /start para volver al menú principal") + return ConversationHandler.END + elif "cancelar" in texto or texto == "❌": + await update.message.reply_text("Servicio cancelado. Si deseas iniciar de nuevo, escribe /start.") + return ConversationHandler.END + else: + await update.message.reply_text("Respuesta inválida. Selecciona *Confirmar* o *Cancelar* desde el menú.") + return THIRD_MENU # Fix: Return to THIRD_MENU instead of MAIN_MENU + + + +# Mi funcion pincipal +def main(): + app = ApplicationBuilder().token(BOT_TOKEN).build() + + conv_handler = ConversationHandler( + entry_points=[CommandHandler("start", start)], + states={ + MAIN_MENU: [MessageHandler(filters.TEXT & ~filters.COMMAND, seleccionar_servicio)], + FIRST_MENU: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_datos)], + SECOND_MENU: [MessageHandler(filters.TEXT & ~filters.COMMAND, autorizacion)], + THIRD_MENU: [MessageHandler(filters.TEXT & ~filters.COMMAND, confirmar)], + }, + fallbacks=[], + ) + + app.add_handler(conv_handler) + print("mi bot ejecutandose") + app.run_polling() + +if __name__ == "__main__": + main() diff --git a/proyecto/geomara_maribel_tambaco_tipantiza/app.py b/proyecto/geomara_maribel_tambaco_tipantiza/app.py new file mode 100644 index 0000000..3a4a9ef --- /dev/null +++ b/proyecto/geomara_maribel_tambaco_tipantiza/app.py @@ -0,0 +1,222 @@ +from telegram import Update, ReplyKeyboardMarkup +from telegram.ext import ( + ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, + filters, ConversationHandler +) +import sqlite3 +import os +import re +import requests +from datetime import datetime +from dotenv import load_dotenv + +load_dotenv() + +BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") +MAILJET_API_KEY = os.getenv("MAILJET_API_KEY") +MAILJET_SECRET_KEY = os.getenv("MAILJET_SECRET_KEY") +MAILJET_URL = os.getenv("MAILJET_URL") +MAILJET_FROM_NAME = os.getenv("MAILJET_FROM_NAME") +EMAIL_FROM = os.getenv("EMAIL_FROM") + +MENU, DATOS, AUTORIZACION, CONFIRMAR = range(4) + +# Cursos de capacitación que ofrece KonKito +CURSOS = { + "1. 📈 Marketing Digital para Emprendedores": 300, + "2. 💼 Gestión Financiera Básica": 250, + "3. 🚀 Estrategias de Crecimiento": 350, + "4. 🛠️ Herramientas para la Productividad": 200 +} + +conn = sqlite3.connect("chat.db", check_same_thread=False) +cursor = conn.cursor() +cursor.execute(""" +CREATE TABLE IF NOT EXISTS chat_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + username TEXT, + curso TEXT, + message_id INTEGER, + timestamp TEXT +) +""") +conn.commit() + +def guardar_interaccion(update: Update): + user = update.effective_user + username = user.username or "Sin username" + texto = update.message.text + message_id = update.message.message_id + timestamp = datetime.now().isoformat() + + cursor.execute(""" + INSERT INTO chat_data (user_id, username, curso, message_id, timestamp) + VALUES (?, ?, ?, ?, ?) + """, (user.id, username, texto, message_id, timestamp)) + conn.commit() + +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): + mensaje = ( + "🚀 Bienvenido al *Bot de Cursos de capacitación de KonKito*.\n\n" + "Selecciona un curso para continuar:" + ) + keyboard = [[curso] for curso in CURSOS.keys()] + await update.message.reply_text( + mensaje, + reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True), + parse_mode="Markdown" + ) + guardar_interaccion(update) + return MENU + +async def seleccionar_curso(update: Update, context: ContextTypes.DEFAULT_TYPE): + curso = update.message.text.strip() + if curso in CURSOS: + context.user_data["curso"] = curso + await update.message.reply_text( + "✍️ Por favor, escribe:\n1. Tu nombre completo\n2. Tu correo electrónico\n3. Una breve descripción de tu emprendimiento\n\nFormato:\n`Vilma Pérez - correo@ejemplo.com - Información de . . .`", + parse_mode="Markdown" + ) + guardar_interaccion(update) + return DATOS + else: + await update.message.reply_text("❌ Opción inválida. Usa el menú para elegir un curso.") + return MENU + +async def recibir_datos(update: Update, context: ContextTypes.DEFAULT_TYPE): + texto = update.message.text.strip() + email = extraer_email(texto) + if not email: + await update.message.reply_text( + "❌ No se detectó un correo válido. Escribe tus datos así:\n`Nombre - correo@ejemplo.com - descripción`", + parse_mode="Markdown" + ) + return DATOS + + context.user_data["datos"] = texto + keyboard = [["✅ Sí", "❌ No"]] + await update.message.reply_text( + "🔐 ¿Autorizas el uso de tus datos para contactarte y procesar tu inscripción?", + reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True) + ) + guardar_interaccion(update) + return AUTORIZACION + +async def autorizacion(update: Update, context: ContextTypes.DEFAULT_TYPE): + respuesta = update.message.text.lower() + if "sí" in respuesta or "si" in respuesta or "✅" in respuesta: + curso = context.user_data["curso"] + precio = CURSOS[curso] + keyboard = [["✅ Confirmar", "❌ Cancelar"]] + await update.message.reply_text( + f"💰 El precio del curso *{curso}* es de *${precio}*.\n¿Deseas confirmar tu inscripción?", + reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True, one_time_keyboard=True), + parse_mode="Markdown" + ) + return CONFIRMAR + elif "no" in respuesta or "❌" in respuesta: + await update.message.reply_text("🚫 No podemos continuar sin tu autorización. Escribe /start para reiniciar.") + return ConversationHandler.END + else: + await update.message.reply_text("❌ Respuesta no válida. Usa el menú.") + return AUTORIZACION + +async def confirmar(update: Update, context: ContextTypes.DEFAULT_TYPE): + texto = update.message.text.lower() + if "confirmar" in texto or "✅" in texto: + curso = context.user_data["curso"] + descripcion = context.user_data["datos"] + correo = extraer_email(descripcion) + + mensaje = ( + f"🎉 *Gracias por inscribirte!*\n\n" + f"✅ *Curso:* {curso}\n" + f"📝 *Descripción:* {descripcion}\n" + f"💵 *Precio:* ${CURSOS[curso]}\n\n" + "Nos pondremos en contacto contigo pronto. 📧" + ) + + enviado = enviar_mailjet(context, correo, "Confirmación de inscripción", mensaje) + if enviado: + await update.message.reply_text("📧 Correo de confirmación enviado con éxito.") + else: + await update.message.reply_text("⚠️ Hubo un error al enviar el correo.") + + await update.message.reply_text("¿Necesitas algo más? Escribe /start para comenzar otra inscripción.") + return ConversationHandler.END + + elif "cancelar" in texto or "❌" in texto: + await update.message.reply_text("❌ Inscripción cancelada. Escribe /start para iniciar de nuevo.") + return ConversationHandler.END + + else: + await update.message.reply_text("❌ Respuesta inválida. Usa el menú.") + return CONFIRMAR + +def extraer_email(texto): + match = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', texto) + return match.group(0) if match else None + +def enviar_mailjet(context, destinatario, asunto, contenido): + data = { + "Messages": [ + { + "From": { + "Email": EMAIL_FROM, + "Name": MAILJET_FROM_NAME + }, + "To": [ + { + "Email": destinatario, + "Name": "Cliente" + } + ], + "Subject": asunto, + "HTMLPart": f""" +
+

🎉 ¡Gracias por tu inscripción!

+

Curso: {context.user_data['curso']}

+

📝 Descripción: {context.user_data['datos']}

+

💵 Precio: ${CURSOS[context.user_data['curso']]}

+

Nos pondremos en contacto contigo pronto.

+
+ """ + } + ] + } + + try: + response = requests.post( + MAILJET_URL, + json=data, + auth=(MAILJET_API_KEY, MAILJET_SECRET_KEY), + timeout=(3.05, 5) + ) + print(f"Mailjet response: {response.status_code} - {response.text}") + return response.status_code == 200 + except Exception as e: + print(f"Error enviando correo con Mailjet: {e}") + return False + +def main(): + app = ApplicationBuilder().token(BOT_TOKEN).build() + + conv_handler = ConversationHandler( + entry_points=[CommandHandler("start", start)], + states={ + MENU: [MessageHandler(filters.TEXT & ~filters.COMMAND, seleccionar_curso)], + DATOS: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_datos)], + AUTORIZACION: [MessageHandler(filters.TEXT & ~filters.COMMAND, autorizacion)], + CONFIRMAR: [MessageHandler(filters.TEXT & ~filters.COMMAND, confirmar)], + }, + fallbacks=[], + ) + + app.add_handler(conv_handler) + + print("🤖 Bot en ejecución...") + app.run_polling() + +if __name__ == "__main__": + main() diff --git a/proyecto/geomara_maribel_tambaco_tipantiza/chat.db b/proyecto/geomara_maribel_tambaco_tipantiza/chat.db new file mode 100644 index 0000000..f51beba Binary files /dev/null and b/proyecto/geomara_maribel_tambaco_tipantiza/chat.db differ diff --git a/proyecto/geomara_maribel_tambaco_tipantiza/ver_datos.py b/proyecto/geomara_maribel_tambaco_tipantiza/ver_datos.py new file mode 100644 index 0000000..14afec9 --- /dev/null +++ b/proyecto/geomara_maribel_tambaco_tipantiza/ver_datos.py @@ -0,0 +1,12 @@ +import sqlite3 + +conn = sqlite3.connect("chat.db") +cursor = conn.cursor() + +cursor.execute("SELECT * FROM chat_data") +rows = cursor.fetchall() + +for row in rows: + print(row) + +conn.close() diff --git a/proyecto/george_penafiel/README.md b/proyecto/george_penafiel/README.md deleted file mode 100644 index 2e559c9..0000000 --- a/proyecto/george_penafiel/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### 📝 Opción Desarrollada - -Este proyecto implementa la **Opción 2: Web App con Flask**. Se trata de un chatbot interactivo que funciona a través de una aplicación web, utilizando el framework Flask para la lógica del servidor y la API de Gemini para las respuestas. El historial de las conversaciones se almacena en una base de datos SQLite. \ No newline at end of file diff --git a/proyecto/george_penafiel/templates/index.html b/proyecto/george_penafiel/templates/index.html deleted file mode 100644 index 6e0076b..0000000 --- a/proyecto/george_penafiel/templates/index.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - 💬 Chatbot con Gemini AI - - - - - -
-
-
- Chatbot con Gemini AI -
-
- {% for user_msg, bot_msg, timestamp in chat_history %} -
- Tú: {{ user_msg }} -
{{ timestamp }}
-
-
- Bot: {{ bot_msg }} -
{{ timestamp }}
-
- {% endfor %} -
- -
- - Limpiar chat - -
- - - - - \ No newline at end of file diff --git a/proyecto/jorge_guato/app.py b/proyecto/jorge_guato/app.py new file mode 100644 index 0000000..268e62d --- /dev/null +++ b/proyecto/jorge_guato/app.py @@ -0,0 +1,178 @@ +import telebot +from telebot.types import ReplyKeyboardMarkup, KeyboardButton, Message +import sqlite3 +import json +import os +from dotenv import load_dotenv + +# Cargar variables de entorno +load_dotenv() +BOT_TOKEN = os.getenv("BOT_TOKEN") + +# Inicializar bot +bot = telebot.TeleBot(BOT_TOKEN) + +# --- Clases --- +class DBHelper: + @staticmethod + def init(): + with sqlite3.connect("chat.db") as conn: + c = conn.cursor() + c.execute(''' + CREATE TABLE IF NOT EXISTS pedidos ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + nombre TEXT, + telefono TEXT, + email TEXT, + direccion TEXT, + productos TEXT, + total REAL, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP + ) + ''') + + @staticmethod + def guardar_pedido(user_id, datos): + with sqlite3.connect("chat.db") as conn: + c = conn.cursor() + c.execute(''' + INSERT INTO pedidos (user_id, nombre, telefono, email, direccion, productos, total) + VALUES (?, ?, ?, ?, ?, ?, ?) + ''', ( + user_id, + datos["nombre"], + datos["telefono"], + datos["email"], + datos["direccion"], + json.dumps(datos["productos"]), + datos["total"] + )) + +class PedidoManager: + ESTADOS = { + "MENU": "menu", + "PEDIDO_NOMBRE": "nombre", + "PEDIDO_TELEFONO": "telefono", + "PEDIDO_EMAIL": "email", + "PEDIDO_DIRECCION": "direccion" + } + + PRODUCTOS = { + "1": {"nombre": "Vino de Uva Premium", "precio": 25.00}, + "2": {"nombre": "Vino de Mortiño", "precio": 30.00}, + "3": {"nombre": "Vino de Arándano", "precio": 28.00}, + "4": {"nombre": "Chocolate Clásico", "precio": 8.00}, + "5": {"nombre": "Chocolate Premium", "precio": 12.00}, + "6": {"nombre": "Chocolate Especial", "precio": 15.00} + } + + usuarios = {} + + @classmethod + def reiniciar(cls, user_id): + cls.usuarios[user_id] = {"estado": cls.ESTADOS["MENU"], "carrito": []} + + @classmethod + def agregar_producto(cls, user_id, codigo): + producto = cls.PRODUCTOS.get(codigo) + if producto: + cls.usuarios[user_id]["carrito"].append(producto) + total = sum(p["precio"] for p in cls.usuarios[user_id]["carrito"]) + cls.usuarios[user_id]["pedido"] = {"productos": cls.usuarios[user_id]["carrito"], "total": total} + cls.usuarios[user_id]["estado"] = cls.ESTADOS["PEDIDO_NOMBRE"] + bot.send_message(user_id, f"🛒 {producto['nombre']} añadido al carrito.") + bot.send_message(user_id, "👤 Ingresa tu nombre completo o escribe 0 para salir:") + else: + bot.send_message(user_id, "Producto no válido.") + +# --- Bot Handlers --- +@bot.message_handler(commands=["start"]) +def start(message: Message): + user_id = message.from_user.id + PedidoManager.reiniciar(user_id) + + markup = ReplyKeyboardMarkup(resize_keyboard=True) + markup.add("1. Ver Catálogo de Vinos", "2. Ver Catálogo de Chocolates", "0. Salir") + + texto = """ +👋 *¡Hola! Bienvenido a La Rústica* 🍷🍫 + +1. Ver Catálogo de Vinos +2. Ver Catálogo de Chocolates +0. Salir + +Elige una opción: + """ + bot.send_message(user_id, texto, reply_markup=markup, parse_mode="Markdown") + +def mostrar_catalogo_vinos(user_id): + texto = "🍷 *Catálogo de Vinos*\n" + texto += "1. Vino de Uva Premium - $25.00\n" + texto += "2. Vino de Mortiño - $30.00\n" + texto += "3. Vino de Arándano - $28.00\n" + texto += "\nEscribe el número del producto para seleccionarlo, o 0 para salir." + bot.send_message(user_id, texto, parse_mode="Markdown") + +def mostrar_catalogo_chocolates(user_id): + texto = "🍫 *Chocolates La Rústica*\n" + texto += "4. Chocolate Clásico - $8.00\n" + texto += "5. Chocolate Premium - $12.00\n" + texto += "6. Chocolate Especial - $15.00\n" + texto += "\nEscribe el número del producto para seleccionarlo, o 0 para salir." + bot.send_message(user_id, texto, parse_mode="Markdown") + +@bot.message_handler(func=lambda m: True) +def handle_message(message: Message): + user_id = message.from_user.id + texto = message.text.strip() + + if user_id not in PedidoManager.usuarios: + PedidoManager.reiniciar(user_id) + + if texto == "0": + PedidoManager.reiniciar(user_id) + bot.send_message(user_id, "❌ Proceso cancelado.") + return start(message) + + estado = PedidoManager.usuarios[user_id]["estado"] + + if estado == PedidoManager.ESTADOS["MENU"]: + if texto == "1. Ver Catálogo de Vinos": + mostrar_catalogo_vinos(user_id) + elif texto == "2. Ver Catálogo de Chocolates": + mostrar_catalogo_chocolates(user_id) + elif texto in PedidoManager.PRODUCTOS: + PedidoManager.agregar_producto(user_id, texto) + else: + bot.send_message(user_id, "Por favor, selecciona una opción del menú.") + + elif estado == PedidoManager.ESTADOS["PEDIDO_NOMBRE"]: + PedidoManager.usuarios[user_id]["pedido"]["nombre"] = texto + PedidoManager.usuarios[user_id]["estado"] = PedidoManager.ESTADOS["PEDIDO_TELEFONO"] + bot.send_message(user_id, "📞 Ingresa tu teléfono o escribe 0 para salir:") + + elif estado == PedidoManager.ESTADOS["PEDIDO_TELEFONO"]: + PedidoManager.usuarios[user_id]["pedido"]["telefono"] = texto + PedidoManager.usuarios[user_id]["estado"] = PedidoManager.ESTADOS["PEDIDO_EMAIL"] + bot.send_message(user_id, "📧 Ingresa tu email o escribe 0 para salir:") + + elif estado == PedidoManager.ESTADOS["PEDIDO_EMAIL"]: + PedidoManager.usuarios[user_id]["pedido"]["email"] = texto + PedidoManager.usuarios[user_id]["estado"] = PedidoManager.ESTADOS["PEDIDO_DIRECCION"] + bot.send_message(user_id, "📍 Ingresa tu dirección o escribe 0 para salir:") + + elif estado == PedidoManager.ESTADOS["PEDIDO_DIRECCION"]: + PedidoManager.usuarios[user_id]["pedido"]["direccion"] = texto + DBHelper.guardar_pedido(user_id, PedidoManager.usuarios[user_id]["pedido"]) + bot.send_message(user_id, "✅ Pedido guardado correctamente. Te contactaremos pronto. 🎉") + PedidoManager.reiniciar(user_id) + start(message) + else: + bot.send_message(user_id, "Usa el menú para continuar.") + +# Iniciar bot +if __name__ == "__main__": + DBHelper.init() + print("🤖 Bot iniciado...") + bot.infinity_polling() diff --git a/proyecto/george_penafiel/chat.db b/proyecto/jorge_guato/chat.db similarity index 56% rename from proyecto/george_penafiel/chat.db rename to proyecto/jorge_guato/chat.db index 6937d0a..d63dbe2 100644 Binary files a/proyecto/george_penafiel/chat.db and b/proyecto/jorge_guato/chat.db differ diff --git a/proyecto/jorge_guato/readme.md b/proyecto/jorge_guato/readme.md new file mode 100644 index 0000000..6d7f27d --- /dev/null +++ b/proyecto/jorge_guato/readme.md @@ -0,0 +1,12 @@ +# Bot de Telegram - La Rustica + +Este proyecto implementa un chatbot en Telegram para una empresa de vinos y chocolates. + +## Funcionalidades + +- Saludo personalizado +- Menú interactivo con productos: + - Vinos de uva, mortiño y arándano + - Chocolates "La Rustica" +- Solicita cantidad y nombre del cliente +- Almacena los pedidos en una base de datos SQLite (`chat.db`) \ No newline at end of file diff --git a/proyecto/jorge_luis_castellanos/app.py b/proyecto/jorge_luis_castellanos/app.py new file mode 100644 index 0000000..30ac87c --- /dev/null +++ b/proyecto/jorge_luis_castellanos/app.py @@ -0,0 +1,59 @@ +from telegram import Update, ReplyKeyboardMarkup +from telegram.ext import ( + ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, + filters, ConversationHandler +) +from mailjet_rest import Client +import requests +import os +import re +import sqlite3 +from datetime import datetime +from dotenv import load_dotenv +from telebot import TeleBot, types +import datetime + +load_dotenv() + +BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") +bot = TeleBot(BOT_TOKEN) + +# Estado temporal de los usuarios +user_data = {} + +@bot.message_handler(commands=['start']) +def start(message): + bot.send_message(message.chat.id, "Hola...\nTe saluda Terra Habitat Bienes Raices\nPor favor, escribe tu nombre completo:") + bot.register_next_step_handler(message, process_nombre) + +def process_nombre(message): + user_data[message.chat.id] = {"nombre": message.text} + + bot.send_message(message.chat.id, "¿Qué servicio deseas? \n (Compra - Venta - Alquiler - Avalúo - Administración - Otro Servicio)") + bot.register_next_step_handler(message, process_servicio) + +def process_servicio(message): + user_data[message.chat.id]["servicio"] = message.text + + bot.send_message(message.chat.id, "Ayudame con tu número de teléfono") + bot.register_next_step_handler(message, process_telefono) + +def process_telefono(message): + user_data[message.chat.id]["telefono"] = message.text + + bot.send_message(message.chat.id, "Ayudame con tu correo electrónico") + bot.register_next_step_handler(message, cita) + +def cita(message): + user_data[message.chat.id]["time"] = message.text + data = user_data[message.chat.id] + + bot.send_message(message.chat.id, "🔹¡Tu cita ha sido registrada!\nUn Asesor te atendera de forma presonalizada") + resumen = f"🗓️*Resumen de tu cita:*\nNombre: {data['nombre']}\nServicio: {data['servicio']}\nTelefono: {data['telefono']}" + + bot.send_message(message.chat.id, resumen, parse_mode='Markdown') + + +if __name__ == "__main__": + print("Bot ejecutándose...") + bot.infinity_polling() \ No newline at end of file diff --git a/proyecto/jorge_luis_castellanos/readme.txt b/proyecto/jorge_luis_castellanos/readme.txt new file mode 100644 index 0000000..611d11c --- /dev/null +++ b/proyecto/jorge_luis_castellanos/readme.txt @@ -0,0 +1,5 @@ +Bot de Telegram + +Este bot crea una cita para contacto automatico con la Api de Telegram para la empresa de bienes raices Terra Habitat +Fue desarrollado con la libreria de Telebot y Telegram +Como caracteristicas futuras, se espera implementar el mismo bot con flask y posiblemente con Whatsapp ya que es el principal canal de comunicacion \ No newline at end of file diff --git a/proyecto/jorge_luis_castellanos/requirements.txt b/proyecto/jorge_luis_castellanos/requirements.txt new file mode 100644 index 0000000..7ce90bd --- /dev/null +++ b/proyecto/jorge_luis_castellanos/requirements.txt @@ -0,0 +1,24 @@ +anyio==4.9.0 +blinker==1.9.0 +certifi==2025.7.14 +charset-normalizer==3.4.2 +click==8.2.1 +Flask==3.1.1 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.10 +itsdangerous==2.2.0 +Jinja2==3.1.6 +mailjet-rest==1.5.1 +MarkupSafe==3.0.2 +pyTelegramBotAPI==4.27.0 +python-dotenv==1.1.1 +python-telegram-bot==22.2 +requests==2.32.4 +sniffio==1.3.1 +telebot==0.0.5 +telegram==0.0.1 +typing_extensions==4.14.1 +urllib3==2.5.0 +Werkzeug==3.1.3 diff --git a/proyecto/jorge_luis_castellanos/telebot_complete.db b/proyecto/jorge_luis_castellanos/telebot_complete.db new file mode 100644 index 0000000..f678dcb Binary files /dev/null and b/proyecto/jorge_luis_castellanos/telebot_complete.db differ diff --git a/proyecto/pablo_colcha.py b/proyecto/pablo_colcha.py deleted file mode 100644 index aa11b25..0000000 --- a/proyecto/pablo_colcha.py +++ /dev/null @@ -1,22 +0,0 @@ -from telebot import TeleBot - -TOKEN_BOT="TOKEN_BOT" -bot = tb.TeleBot(TOKEN_BOT) - -@bot.message_handler(commands=['start']) -def start(message): - print("Entro al start del bot") - bot.send_message(message.chat.id, "Bienbenido a la tienda de pablo ") - bot.register_next_step_handler(message,menu) - -def menu(message): - bot.send_message(message.chat.id, "Estos son lo paquetes que tenemos:\n 1. Paquete 1\n 2. Paquete 2\n 3. Paquete 3") - bot.register_next_step_handler(message, opciones) - -def opciones(message): - print(message) - bot.send_message(message.chat.id, "opcion no valida") - bot.register_next_step_handler(message) -if __name__ == '__main__': - print ("Bot iniciado") - bot.infinity_polling() \ No newline at end of file diff --git a/proyecto/proyecto-carlos_bodero/app.py b/proyecto/proyecto-carlos_bodero/app.py new file mode 100644 index 0000000..ffea971 --- /dev/null +++ b/proyecto/proyecto-carlos_bodero/app.py @@ -0,0 +1,111 @@ +import os +import google.generativeai as genai +import smtplib +from email.message import EmailMessage +from flask import Flask, render_template, request, jsonify +from dotenv import load_dotenv +from sqlalchemy import create_engine, Column, Integer, String, Text +from sqlalchemy.orm import declarative_base, sessionmaker + +# Carga variables de entorno +load_dotenv() +GEMINI_API_KEY = os.getenv("API_KEY_GEMINI") +USUARIO_CORREO = os.getenv("USUARIO_CORREO") +CONTRASENA_CORREO = os.getenv("CONTRASENA_CORREO") + + +if not GEMINI_API_KEY: + raise Exception("La API Key de Gemini no está configurada en el archivo .env.") + + +modelo_en_uso = None + +modelos = [ + "gemini-2.5-flash", + "gemini-2.5-flash-latest", + "gemini-2.5-flash-002", + "gemini-2.5-pro", + "gemini-2.5-pro-latest", + "gemini-2.5-pro-002", + "gemini-1.5-flash", + "gemini-1.5-flash-latest", + "gemini-1.5-flash-002", + "gemini-1.5-pro", + "gemini-1.5-pro-latest", + "gemini-1.5-pro-002", +] + +# Datos de tu cuenta Gmail +usuario_mail = USUARIO_CORREO +contrasena = CONTRASENA_CORREO + +genai.configure(api_key=GEMINI_API_KEY) +modelo_en_uso = modelos[0] + +# Flask y DB setup +app = Flask(__name__) +#DATABASE_URL = "sqlite:///chat.db" +DATABASE_URL = "sqlite:///proyecto/proyecto-carlos_bodero/chat.db" +engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}) +Base = declarative_base() + +# Modelo de Chat +class Chat(Base): + __tablename__ = 'chat' + id = Column(Integer, primary_key=True) + usuario_nombre = Column(String(100)) + correo = Column(String(100)) + texto = Column(Text) + +Base.metadata.create_all(engine) +Session = sessionmaker(bind=engine) +db_session = Session() + +def consultar_gemini(mensaje): + model = genai.GenerativeModel(modelo_en_uso) + try: + response = model.generate_content(mensaje) + return response.text + except Exception as e: + return f"Error consultando Gemini: {str(e)}" + +def enviarCorreo(usuario,correo,mensaje): + # Configuración del mensaje + msg = EmailMessage() + msg['Subject'] = "Chat " + str(usuario) + msg['From'] = 'monitorcbo4@gmail.com' + msg['To'] = str(correo) + msg.set_content(mensaje) + try: + with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp: + smtp.login(usuario_mail, contrasena) + smtp.send_message(msg) + print("Correo enviado correctamente.") + except Exception as e: + print("Error al enviar el correo:", e) + +@app.route("/") +def index(): + return render_template("index.html") + +@app.route("/chat", methods=["POST"]) +def chat(): + data = request.json + user_message = data.get("message") + respuesta_gemini = consultar_gemini(user_message) + return jsonify({"response": respuesta_gemini}) + +@app.route("/guardar", methods=["POST"]) +def guardar(): + data = request.json + nombre = data.get("nombre") + correo = data.get("email") + texto_chat = data.get("chat") + chat_entry = Chat(usuario_nombre=nombre, correo=correo, texto=texto_chat) + db_session.add(chat_entry) + db_session.commit() + enviarCorreo(nombre,correo,texto_chat) + return jsonify({"status": "ok"}) + +if __name__ == "__main__": + app.run("0.0.0.0",debug=True) diff --git a/proyecto/proyecto-carlos_bodero/chat.db b/proyecto/proyecto-carlos_bodero/chat.db new file mode 100644 index 0000000..23973e5 Binary files /dev/null and b/proyecto/proyecto-carlos_bodero/chat.db differ diff --git a/proyecto/proyecto-carlos_bodero/readme.md b/proyecto/proyecto-carlos_bodero/readme.md new file mode 100644 index 0000000..091ce78 --- /dev/null +++ b/proyecto/proyecto-carlos_bodero/readme.md @@ -0,0 +1,95 @@ +# Chat Gemini con Flask y SQLite + +Este proyecto es una aplicación web de chat construida con **Flask** que permite interactuar con la IA de **Gemini** (Google). La aplicación almacena cada conversación en una base de datos **SQLite**, guardando el nombre, correo electrónico del usuario y el historial del chat. + +--- + +## Características + +- Interfaz web amigable para chatear con Gemini. +- Campos para ingresar **nombre** y **correo electrónico** del usuario. +- Al finalizar la conversación, se almacena todo el chat en SQLite. +- Integración con la API oficial de Gemini (`google-generativeai`). + +--- + +## Requisitos + +- Python 3.8 o superior +- Cuenta de Google para obtener la API Key de Gemini + +--- + +## Instalación + +1. **Clona este repositorio** o descarga el código. + +2. **Instala las dependencias**: + + ```bash + pip install flask google-generativeai python-dotenv sqlalchemy + ``` + +3. **Obtén tu API Key de Gemini**: + + - Accede a [Google AI Studio](https://aistudio.google.com/app/apikey) + - Inicia sesión con tu cuenta Google + - Haz clic en “Create API key” y copia el valor + +4. **Configuración archivo `.env`**: + + + ``` + API_KEY_GEMINI=api_key_de_gemini + USUARIO_MAIL= contraseña GMAIL + CONTRASENA_MAIL=contraseña de aplicacion GMAIL + ``` + +--- + +## Uso + +1. Ejecuta la aplicación: + + ```bash + python app.py + ``` + +2. Abre tu navegador en [http://127.0.0.1:5000](http://127.0.0.1:5000) + +3. Ingresa tu nombre y correo electrónico, chatea con Gemini y al finalizar haz clic en el botón **Limpiar** para guardar la conversación. + +4. Las conversaciones se almacenan en `chat.db` en la tabla `chat`. + +--- + +## Estructura de la base de datos + +Tabla: **chat** + +| Campo | Tipo | Descripción | +|-----------------|----------|---------------------------------------| +| id | Integer | Clave primaria (autoincremental) | +| usuario_nombre | String | Nombre del usuario | +| correo | String | Correo electrónico del usuario | +| texto | Text | Conversación completa (historial chat)| + + +--- + +## Seguridad + + +- Las variables de entorno globales en el archivo `.env` `.gitignore`. + +--- + +## Créditos + +Desarrollado por Carlos Bodero. + +--- + +## Licencia + +Este proyecto se proporciona bajo la licencia MIT. Puedes modificar y usar el código libremente. diff --git a/proyecto/proyecto-carlos_bodero/requirements.txt b/proyecto/proyecto-carlos_bodero/requirements.txt new file mode 100644 index 0000000..5eaf725 --- /dev/null +++ b/proyecto/proyecto-carlos_bodero/requirements.txt @@ -0,0 +1,2 @@ +flask +requests \ No newline at end of file diff --git a/proyecto/proyecto-carlos_bodero/templates/index.html b/proyecto/proyecto-carlos_bodero/templates/index.html new file mode 100644 index 0000000..8b42d3f --- /dev/null +++ b/proyecto/proyecto-carlos_bodero/templates/index.html @@ -0,0 +1,120 @@ + + + + + Chat con Gemini + + + +
+

Chat con Gemini IA

+
+ + +
+
+ + + +
+ + + diff --git a/proyecto/readme.md b/proyecto/readme.md deleted file mode 100644 index ea8d743..0000000 --- a/proyecto/readme.md +++ /dev/null @@ -1,175 +0,0 @@ -# 🧠 Proyecto Final – Chatbot con Python - -## 🚀 Descripción General - - Este proyecto tiene como objetivo aplicar todos los conocimientos adquiridos durante el curso, desarrollando un chatbot funcional que puede ejecutarse en Telegram o desde una interfaz web utilizando Flask. El bot debe conectarse a una API de inteligencia artificial (como Gemini) y almacenar sus interacciones en una base de datos. Además, debe incluir una acción adicional como enviar correos, generar una imagen o exportar información. - -## 🎯 Objetivo del Proyecto - - Crear un chatbot inteligente que interactúe con usuarios, responda preguntas, tome decisiones o realice acciones específicas (como agendar una cita, enviar un mensaje de confirmación o generar una imagen). Puedes elegir una de estas dos opciones: - -### Opción 1 – 🤖 Bot de Telegram - - Desarrolla un chatbot en Telegram usando `python-telegram-bot` que: - - Salude al usuario - - Ofrezca un menú de servicios - - Tome datos de contacto o pedidos - - Envíe mensajes de confirmación o genere imágenes - - Guarde las conversaciones en SQLite o JSON - -### Opción 2 – 🌐 Web App con Flask - - Crea una aplicación SPA (Single Page App) que: - - Permita al usuario enviar preguntas - - Se conecte con una IA (Gemini API, HuggingFace, etc.) - - Muestre respuestas como un chat web - - Guarde las conversaciones en SQLite - - Permita limpiar el historial y generar confirmaciones - - --- - -## 📁 Organización del Proyecto - - 1. Dentro de la carpeta `proyecto`, crea una carpeta con tu nombre y apellido. - - proyecto/ - ├── nombre_apellido/ - │ ├── app.py - │ ├── templates/ - │ │ └── index.html (solo para Flask) - │ ├── static/ - │ │ └── styles.css (opcional) o puedes usar Boostrap - │ ├── chat.db - │ ├── .env - │ └── README.md - - - 2. Coloca tu código y recursos dentro de esa carpeta. - 3. Indica claramente en tu `README.md` qué opción desarrollaste (Telegram o Flask). - - --- - -## 🧰 Requisitos Generales - -### Librerías Comunes (para ambos proyectos) - - ```bash - pip install python-dotenv - ``` - -## 🚀 Opción 1: Telegram Bot - -### 🧩 Librerías necesarias - - ```bash - pip install python-telegram-bot - pip install python-dotenv - ``` - -### 🔐 .env (ejemplo) - - ```bash - TELEGRAM_BOT_TOKEN=tu_token_de_telegram - ``` - -### 📌 Pasos - - 1. **Crea tu bot en Telegram** usando [@BotFather](https://t.me/BotFather) y obtén tu token. - 2. **Instala las dependencias necesarias**: - - ```bash - pip install python-telegram-bot python-dotenv sqlite3 - ``` - - 3. **Crea un archivo .env y agrega tu token**: - - ```bash - TELEGRAM_BOT_TOKEN=TU_TOKEN_AQUI - ``` - - 4. **Guardar cada interacción en SQLite (usuario, mensaje, hora)**. - - 5. **Agregar una acción adicional:** - - 6. **Enviar correo de confirmación** - - 7. **Generar imagen o PDF** - -## 🚀 Opción 2: Flask + AI - -### 🧩 Librerías necesarias - - ```bash - pip install Flask google-generativeai python-dotenv - ``` - -### 🔐 .env (ejemplo) - - ```bash - API_KEY_GEMINI=tu_clave_api_gemini - ``` - -### 📌 Pasos - - 1. **Crear entorno virtual:** - - ```bash - python -m venv venv - source venv/bin/activate # Windows: venv\Scripts\activate - ``` - - 2. **Crear archivo app.py y cargar la clave desde .env**: - - 3. **Crear formularios HTML + Bootstrap para enviar mensajes**: - - 4. **Procesar las preguntas con Gemini y mostrar respuestas.** - - 5. **Guardar los mensajes en SQLite** - - 6. **Agregar acción adicional:** - - 7. **Generar imagen o PDF, enviar correo** - -## ✅ Requisitos para Aprobar - -| Requisito | Obligatorio | -|--------------------------------------|-------------| -| Chat funcional | ✅ | -| Conexión con API externa | ✅ | -| Base de datos funcional | ✅ | -| Acción extra (correo, imagen, etc.) | ✅ | -| Documentación en README.md | ✅ | -| Carpeta con nombre del estudiante | ✅ | - ---- - -## 📰 Entrega - -1. Crear una carpeta con tu nombre completo en la carpeta `proyecto/`. -2. Agregar código fuente, plantilla HTML (si aplica), `.env.example`, base de datos y `README.md`. -3. Hacer fork del repositorio oficial. -4. Subir tus cambios (commit & push). -5. Crear Pull Request con el título: - > Proyecto - TuNombre TuApellido - ---- - -## 🚀 Recomendaciones - -- Investiga APIs gratuitas para mejorar tu bot (HuggingFace, Fun Translations, etc.). -- Usa `flask-mail` para enviar correos desde Flask. -- Usa `sqlite3`, `pandas`, `reportlab` o `Pillow` para guardar y mostrar datos. -- Usa `dotenv` para evitar exponer tus tokens. -- Agrega paginación, exportación o estilo profesional a tu app. - ---- - -## 📈 Inspiración - -Este proyecto te prepara para desarrollar sistemas reales de atención al cliente, asistentes inteligentes o integraciones en aplicaciones productivas. - -Pon en práctica tus conocimientos de Python, APIs, bases de datos, interfaces y comunicación con usuarios. - ---- - -✨ ¡Mucho éxito y manos al código! ✨ diff --git a/proyecto/ronald_diaz/chat.db b/proyecto/ronald_diaz/chat.db new file mode 100644 index 0000000..bd57465 Binary files /dev/null and b/proyecto/ronald_diaz/chat.db differ diff --git a/proyecto/george_penafiel/app.py b/proyecto/ronald_diaz/inicio.py similarity index 71% rename from proyecto/george_penafiel/app.py rename to proyecto/ronald_diaz/inicio.py index 516f2ac..6910947 100644 --- a/proyecto/george_penafiel/app.py +++ b/proyecto/ronald_diaz/inicio.py @@ -1,4 +1,3 @@ - from flask import Flask, render_template, request, redirect, url_for import sqlite3 import os @@ -6,21 +5,16 @@ import google.generativeai as ai load_dotenv() -API_KEY = os.getenv("API_KEY_GEMINI") - -if not API_KEY: - raise ValueError("La API_KEY_GEMINI no está configurada en el archivo .env") - -ai.configure(api_key=API_KEY) +API = os.getenv("API_KEY") +ai.configure(api_key=API) preferred_models = [ "gemini-2.5-flash", - "gemini-2.5-flash-latest", - "gemini-2.5-flash-002", + "gemini-2.5-flash-latest", + "gemini-2.5-flash-002", "gemini-1.5-flash", "gemini-1.5-flash-latest", "gemini-1.5-flash-002", - ] model_to_use = None @@ -34,16 +28,16 @@ break if not model_to_use: - raise Exception("No se encontró un modelo Gemini adecuado que soporte 'generateContent' de la lista preferida.") + raise Exception("No Gemini model available for generateContent") model = ai.GenerativeModel(model_to_use) chat = model.start_chat() - +# --- Flask Setup --- app = Flask(__name__) DATABASE = 'chat.db' - +# --- Database Helper Functions --- def init_db(): conn = sqlite3.connect(DATABASE) c = conn.cursor() @@ -67,6 +61,11 @@ def index(): c.execute("SELECT user_message, bot_response, timestamp FROM messages ORDER BY id ASC") chat_history = c.fetchall() conn.close() + try: + promptInicial = "Actua como un experto en recetas de postres segun el nombre del postre que ingresen debes brindar informacion sobre la receta y preparacion, la respuesta debe ser en español, en cuanto a la preparación que que la explicacion sea bien corta y entendible. Si te preguntan sobre algo que no sea un postre debes presentar el siguiente mensaje: Solo respondo respuestas sobre postres y su preparación" + chat.send_message(promptInicial) + except Exception as e: + print(f"Error: {e}") return render_template('index.html', chat_history=chat_history) @app.route('/send', methods=['POST']) @@ -95,5 +94,9 @@ def clear(): conn.close() return redirect(url_for('index')) +@app.route('/acerca') +def acercaDE(): + return render_template("acerca.html") + if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file + app.run(debug=True) diff --git a/proyecto/ronald_diaz/templates/acerca.html b/proyecto/ronald_diaz/templates/acerca.html new file mode 100644 index 0000000..47856ca --- /dev/null +++ b/proyecto/ronald_diaz/templates/acerca.html @@ -0,0 +1,67 @@ + + + + + Acerca de – PostresChat + + + + + + + + +
+
+

Acerca de PostresChat

+

+ PostresChat es un chatbot especializado en dulces y postres. + Diseñado para responder cualquier pregunta relacionada con la selección de ingredientes, técnicas de preparación, tiempos de horneado y decoración de postres. +

+

+ Ejemplos de temas que cubre: +

    +
  • Cómo hacer un bizcocho esponjoso de vainilla
  • +
  • Alternativas sin gluten o sin lácteos
  • +
  • Técnicas de baño de chocolate y glaseado
  • +
  • Decoración con glaseado real y fondant
  • +
+

+

+ Nota: Si le preguntas algo que no tenga que ver con postres o preparación de dulces, + PostresChat te indicará amablemente que solo puede responder consultas relacionadas a postres. +

+
+
+ + + + + + \ No newline at end of file diff --git a/proyecto/ronald_diaz/templates/index.html b/proyecto/ronald_diaz/templates/index.html new file mode 100644 index 0000000..46cfeac --- /dev/null +++ b/proyecto/ronald_diaz/templates/index.html @@ -0,0 +1,92 @@ + + + + + 🤖 Chatbot con Gemini + + + + + + + +
+
+
+ {% for user_msg, bot_msg, timestamp in chat_history %} +
+
Tú: {{ user_msg }}
+
{{ timestamp }}
+
+
+
Bot: {{ bot_msg }}
+
{{ timestamp }}
+
+ {% endfor %} +
+ +
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/proyecto/valeria_ramos/app.py b/proyecto/valeria_ramos/app.py new file mode 100644 index 0000000..3852d3b --- /dev/null +++ b/proyecto/valeria_ramos/app.py @@ -0,0 +1,239 @@ +from telegram import ( + Update, InlineKeyboardButton, InlineKeyboardMarkup +) +from telegram.ext import ( + ApplicationBuilder, CommandHandler, CallbackQueryHandler, + MessageHandler, ContextTypes, filters, ConversationHandler +) +import requests +import os +import re +import sqlite3 +from datetime import datetime +from dotenv import load_dotenv + +# 📌 Cargar .env +load_dotenv() +BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") +MAILJET_API_KEY = os.getenv("MAILJET_API_KEY") +MAILJET_SECRET_KEY = os.getenv("MAILJET_SECRET_KEY") +MAILJET_URL = os.getenv("MAILJET_URL") +EMAIL_FROM = os.getenv("EMAIL_FROM") + +# 📌 Estados +MENU, DATOS, AUTORIZACION, CONFIRMAR = range(4) + +# 📌 Servicios de Bodega Palermo +SERVICIOS = { + "🍷 Vinos": 25, + "🥃 Licores": 30, + "🍺 Cervezas": 15, + "🍾 Espumantes": 40 +} + +# 📌 Base de datos +conn = sqlite3.connect("bodega_palermo.db", check_same_thread=False) +cursor = conn.cursor() +cursor.execute(""" +CREATE TABLE IF NOT EXISTS chat_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + username TEXT, + servicio TEXT, + message_id INTEGER, + timestamp TEXT +) +""") +conn.commit() + +def guardar_interaccion(update: Update): + user = update.effective_user + username = user.username or "Sin username" + servicio = "" + message_id = None + + if update.callback_query: + servicio = update.callback_query.data + message_id = update.callback_query.message.message_id + elif update.message: + servicio = update.message.text + message_id = update.message.message_id + + timestamp = datetime.now().isoformat() + cursor.execute(""" + INSERT INTO chat_data (user_id, username, servicio, message_id, timestamp) + VALUES (?, ?, ?, ?, ?) + """, (user.id, username, servicio, message_id, timestamp)) + conn.commit() + +# 📌 Inicio con menú interactivo +async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): + mensaje = ( + "🍷 *Bienvenido a Bodega Palermo* 🍾\n\n" + "Selecciona el producto que deseas consultar por favor:" + ) + keyboard = [ + [InlineKeyboardButton(text=opcion, callback_data=opcion)] + for opcion in SERVICIOS.keys() + ] + reply_markup = InlineKeyboardMarkup(keyboard) + + await update.message.reply_text( + mensaje, + reply_markup=reply_markup, + parse_mode="Markdown" + ) + return MENU + +# 📌 Selección del producto +async def servicio_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + await query.answer() + servicio = query.data + context.user_data["servicio"] = servicio + + await query.message.reply_text( + f"📋 Has seleccionado *{servicio}*.\n\n" + f"Por favor, indícanos:\n1. Tu nombre completo\n2. Tu correo electrónico\n3. Detalles del pedido", + parse_mode="Markdown" + ) + guardar_interaccion(update) + return DATOS + +# 📌 Recolección de datos +async def recibir_datos(update: Update, context: ContextTypes.DEFAULT_TYPE): + texto = update.message.text.strip() + email = extraer_email(texto) + if not email: + await update.message.reply_text( + "❌ No detectamos un correo válido.\nPor favor escribe:\n`Nombre - correo@ejemplo.com - detalles del pedido`", + parse_mode="Markdown" + ) + return DATOS + + context.user_data["datos"] = texto + + await update.message.reply_text( + "🔐 ¿Autorizas a Bodega Palermo a usar tus datos para procesar tu pedido?", + reply_markup=InlineKeyboardMarkup([ + [InlineKeyboardButton("✅ Sí", callback_data="si")], + [InlineKeyboardButton("❌ No", callback_data="no")] + ]) + ) + guardar_interaccion(update) + return AUTORIZACION + +# 📌 Autorización +async def autorizacion_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + await query.answer() + respuesta = query.data.lower() + + if "si" in respuesta: + servicio = context.user_data["servicio"] + precio = SERVICIOS[servicio] + await query.message.reply_text( + f"💵 El precio estimado de *{servicio}* es de *${precio}* por unidad.\n\n¿Deseas confirmar tu pedido?", + parse_mode="Markdown", + reply_markup=InlineKeyboardMarkup([ + [InlineKeyboardButton("✅ Confirmar", callback_data="confirmar")], + [InlineKeyboardButton("❌ Cancelar", callback_data="cancelar")] + ]) + ) + guardar_interaccion(update) + return CONFIRMAR + else: + await query.message.reply_text( + "🚫 No podemos continuar sin tu autorización.\nUsa /start para iniciar de nuevo." + ) + return ConversationHandler.END + +# 📌 Confirmación +async def confirmar_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + await query.answer() + accion = query.data.lower() + + if "confirmar" in accion: + servicio = context.user_data["servicio"] + descripcion = context.user_data["datos"] + precio = SERVICIOS[servicio] + correo = extraer_email(descripcion) + + mensaje = ( + f"🍷 *Bodega Palermo* 🍾\n\n" + f"✅ *Producto:* {servicio}\n" + f"📝 *Detalles:* {descripcion}\n" + f"💵 *Precio estimado:* ${precio}\n\n" + "Nos pondremos en contacto contigo pronto. ¡Gracias por tu pedido!" + ) + + if correo: + enviado = enviar_mailjet(context, correo, f"Confirmación de pedido - Bodega Palermo", mensaje) + if enviado: + await query.message.reply_text("📧 Correo de confirmación enviado ✅") + else: + await query.message.reply_text("⚠️ Ocurrió un error al enviar el correo.") + else: + await query.message.reply_text("⚠️ Correo no detectado correctamente.") + + await query.message.reply_text("🍾 Si deseas realizar otro pedido, usa /start.") + guardar_interaccion(update) + return ConversationHandler.END + + else: + await query.message.reply_text( + "❌ Pedido cancelado. Usa /start para comenzar de nuevo cuando quieras." + ) + return ConversationHandler.END + +# 📌 Extraer correo +def extraer_email(texto): + match = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', texto) + return match.group(0) if match else None + +# 📌 Enviar correo +def enviar_mailjet(context, destinatario, asunto, contenido): + data = { + "Messages": [ + { + "From": {"Email": EMAIL_FROM, "Name": "Bodega Palermo"}, + "To": [{"Email": destinatario}], + "Subject": asunto, + "TextPart": contenido + } + ] + } + try: + response = requests.post( + MAILJET_URL, + json=data, + auth=(MAILJET_API_KEY, MAILJET_SECRET_KEY) + ) + print(f"Mailjet: {response.status_code} - {response.text}") + return response.status_code == 200 + except Exception as e: + print(f"❌ Error: {e}") + return False + +# 📌 Main +def main(): + app = ApplicationBuilder().token(BOT_TOKEN).build() + + conv_handler = ConversationHandler( + entry_points=[CommandHandler("start", start)], + states={ + MENU: [CallbackQueryHandler(servicio_callback)], + DATOS: [MessageHandler(filters.TEXT & ~filters.COMMAND, recibir_datos)], + AUTORIZACION: [CallbackQueryHandler(autorizacion_callback)], + CONFIRMAR: [CallbackQueryHandler(confirmar_callback)], + }, + fallbacks=[], + ) + + app.add_handler(conv_handler) + print("🍾 Bot Bodega Palermo en ejecución...") + app.run_polling() + +if __name__ == "__main__": + main() diff --git a/proyecto/valeria_ramos/bodega_palermo.db b/proyecto/valeria_ramos/bodega_palermo.db new file mode 100644 index 0000000..ad981c3 Binary files /dev/null and b/proyecto/valeria_ramos/bodega_palermo.db differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..734e6a5 Binary files /dev/null and b/requirements.txt differ