File size: 11,596 Bytes
09b8bec
 
25d1401
 
cbb526b
 
d3c442c
09b8bec
d3c442c
 
 
 
cbb526b
 
 
 
25d1401
09b8bec
25d1401
09b8bec
 
25d1401
 
d3c442c
09b8bec
 
 
 
25d1401
09b8bec
 
d3c442c
bed04bb
09b8bec
 
 
645e9ba
 
 
 
 
 
09b8bec
cbb526b
 
d3c442c
09b8bec
 
 
d3c442c
 
 
b0b32cd
645e9ba
25d1401
09b8bec
cbb526b
 
bed04bb
cbb526b
645e9ba
 
 
 
 
 
cbb526b
 
 
 
 
 
09b8bec
 
 
0de7167
 
 
 
 
 
 
 
 
 
 
09b8bec
 
 
 
 
 
 
 
 
 
 
0de7167
 
 
09b8bec
 
 
 
 
 
 
 
 
a151314
 
 
5a284ec
cbb526b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70cd3d2
 
09b8bec
d3c442c
 
 
 
a151314
09b8bec
d3c442c
b0b32cd
d3c442c
 
 
 
a151314
09b8bec
d3c442c
 
 
 
 
 
a151314
d3c442c
a151314
d3c442c
 
 
 
 
 
 
a151314
d3c442c
 
a151314
d3c442c
 
09b8bec
 
a151314
09b8bec
a151314
09b8bec
 
d3c442c
 
a151314
 
c20a805
 
 
a151314
 
 
c20a805
 
 
a151314
cbb526b
 
bed04bb
 
c20a805
a151314
c20a805
 
 
5d69377
 
 
 
c20a805
 
 
 
 
 
b689cfd
 
 
 
5d69377
 
 
 
 
b689cfd
1eab75b
b689cfd
5d69377
b689cfd
 
09b8bec
b689cfd
5d69377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1eab75b
5d69377
 
 
 
b689cfd
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
from dotenv import load_dotenv
import streamlit as st
import os
import google.generativeai as genai
import time
import datetime
from ads_formulas import ads_formulas  # Import the ads formulas
from style import styles
from prompts import create_fb_ad_instruction
from emotional_angles import emotional_angles
from copywriter_personas import copywriter_personas
from ad_objectives import ad_objectives  # Import ad objectives
import PyPDF2
import docx
from PIL import Image
import io

# Cargar las variables de entorno
load_dotenv()

# Configurar la API de Google
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))

# Función para generar modelo
@st.cache_resource
def get_model(temperature):
    generation_config = {
        "temperature": temperature,
    }
    return genai.GenerativeModel('gemini-2.0-flash', generation_config=generation_config)

# Function to generate Facebook ads
def generate_fb_ad(target_audience, product, temperature, selected_formula, selected_angle, selected_persona, story_prompt="", ad_objective=None, file_content="", image_parts=None, max_file_chars=50000):
    if not target_audience or not product:
        return "Por favor, completa todos los campos requeridos."
    
    # Enfatizar el tema de la historia si se proporciona
    emphasized_story_prompt = story_prompt
    if story_prompt and story_prompt.strip():
        # Añadir énfasis al tema para que el modelo le dé más importancia
        emphasized_story_prompt = story_prompt.strip()
    
    model = get_model(temperature)
    
    # Crear la instrucción base
    ad_instruction = create_fb_ad_instruction(
        target_audience, 
        product, 
        selected_formula,
        selected_angle,
        selected_persona,
        ad_objective,
        language="español",  # Fixed to Spanish
        story_prompt=emphasized_story_prompt  # Usar el tema enfatizado
    )
    
    # Si hay contenido de archivo, añadirlo a la instrucción
    if file_content:
        ad_instruction += f"\n\nAdemás, utiliza la siguiente información como referencia para crear el anuncio:\n\n{file_content[:max_file_chars]}"
    
    # Si hay un tema específico, ajustar la temperatura para mayor coherencia
    effective_temperature = temperature
    if story_prompt and story_prompt.strip():
        # Reducir ligeramente la temperatura para mantener más enfoque en el tema
        effective_temperature = max(0.1, temperature * 0.9)
    
    # Generar el contenido con o sin imagen
    if image_parts:
        response = model.generate_content([ad_instruction, image_parts], generation_config={"temperature": effective_temperature})
    else:
        response = model.generate_content([ad_instruction], generation_config={"temperature": effective_temperature})
    
    return response.parts[0].text if response and response.parts else "Error generating content."

# Configurar la interfaz de usuario con Streamlit
st.set_page_config(page_title="CopyLegend AI", layout="wide")

# Ocultar el menú de tres puntos de Streamlit
hide_menu_style = """
        <style>
        #MainMenu {visibility: hidden;}
        header {visibility: hidden;}
        footer {visibility: hidden;}
        </style>
        """
st.markdown(hide_menu_style, unsafe_allow_html=True)

# Leer el contenido del archivo manual.md
with open("manual.md", "r", encoding="utf-8") as file:
    manual_content = file.read()

# Mostrar el contenido del manual en el sidebar
st.sidebar.markdown(manual_content)

# Ocultar elementos de la interfaz
st.markdown(styles["main_layout"], unsafe_allow_html=True)

# Centrar el título y el subtítulo con el nuevo texto
st.markdown("<h1 style='text-align: center;'>CopyLegend AI</h1>", unsafe_allow_html=True)
st.markdown("<h4 style='text-align: center;'>La única herramienta que combina la sabiduría de los maestros del copywriting con la precisión de la inteligencia artificial</h4>", unsafe_allow_html=True)

# Añadir CSS personalizado para el botón
st.markdown(styles["button"], unsafe_allow_html=True)

# Crear columnas
col1, col2 = st.columns([1, 2])  

# Columnas de entrada
with col1:
    ad_target_audience = st.text_input("¿Quién es tu público objetivo?", placeholder="Ejemplo: Madres trabajadoras de 30-45 años")
    ad_product = st.text_input("¿Qué producto tienes en mente?", placeholder="Ejemplo: Curso de gestión del tiempo")
    input_prompt = st.text_area("Escribe de qué quieres que trate la historia:", placeholder="Escribe aquí tu idea...")
    
    # Añadir cargador de archivos
    uploaded_file = st.file_uploader("📄 Sube un archivo o imagen de referencia", 
                                    type=['txt', 'pdf', 'docx', 'jpg', 'jpeg', 'png'])
    
    file_content = ""
    is_image = False
    image_parts = None
    
    if uploaded_file is not None:
        file_type = uploaded_file.name.split('.')[-1].lower()
        
        # Manejar archivos de texto
        if file_type in ['txt', 'pdf', 'docx']:
            if file_type == 'txt':
                try:
                    file_content = uploaded_file.read().decode('utf-8')
                except Exception as e:
                    st.error(f"Error al leer el archivo TXT: {str(e)}")
                    file_content = ""
                
            elif file_type == 'pdf':
                try:
                    pdf_reader = PyPDF2.PdfReader(uploaded_file)
                    file_content = ""
                    for page in pdf_reader.pages:
                        file_content += page.extract_text() + "\n"
                except Exception as e:
                    st.error(f"Error al leer el archivo PDF: {str(e)}")
                    file_content = ""
                    
            elif file_type == 'docx':
                try:
                    doc = docx.Document(uploaded_file)
                    file_content = "\n".join([para.text for para in doc.paragraphs])
                except Exception as e:
                    st.error(f"Error al leer el archivo DOCX: {str(e)}")
                    file_content = ""
        
        # Manejar archivos de imagen
        elif file_type in ['jpg', 'jpeg', 'png']:
            try:
                image = Image.open(uploaded_file)
                image_bytes = uploaded_file.getvalue()
                image_parts = [
                    {
                        "mime_type": uploaded_file.type,
                        "data": image_bytes
                    }
                ]
                is_image = True
            except Exception as e:
                st.error(f"Error al procesar la imagen: {str(e)}")
                is_image = False
    
    # Mover el botón aquí, después del cargador de archivos
    submit_ad = st.button("GENERAR ANUNCIO")
    
    with st.expander("Opciones avanzadas"):
        # Selector de fórmula de anuncio
        ad_formula_key = st.selectbox(
            "Fórmula de anuncio",
            options=list(ads_formulas.keys()),
            label_visibility="visible"
        )
        ad_formula = ads_formulas[ad_formula_key]
        
        # Selector de ángulo emocional
        emotional_angle_key = st.selectbox(
            "Ángulo emocional",
            options=list(emotional_angles.keys()),
            label_visibility="visible"
        )
        emotional_angle = emotional_angles[emotional_angle_key]
        
        # Selector de personalidad de copywriter
        ad_persona_key = st.selectbox(
            "Estilo de copywriter",
            options=list(copywriter_personas.keys()),
            index=0,
            format_func=lambda x: x.replace("_", " "),
            label_visibility="visible"
        )
        ad_persona = copywriter_personas[ad_persona_key]
        
        # Selector de objetivo de anuncio
        ad_objective_key = st.selectbox(
            "Objetivo de la campaña",
            options=list(ad_objectives.keys()),
            label_visibility="visible"
        )
        selected_objective = ad_objectives[ad_objective_key] if ad_objective_key != "ninguno" else None

        ad_temperature = st.slider(
            "Nivel de creatividad",
            min_value=0.0, 
            max_value=2.0, 
            value=1.0, 
            step=0.1,
            label_visibility="visible"
        )

# Mostrar el anuncio generado
if submit_ad:
    if ad_target_audience and ad_product:
        # Mostrar el spinner en la segunda columna
        with col2:
            with st.spinner('Generando anuncio...'):
                generated_ad = generate_fb_ad(
                    ad_target_audience, 
                    ad_product, 
                    ad_temperature, 
                    ad_formula,
                    emotional_angle,
                    ad_persona,
                    input_prompt,  # Pass the new story prompt
                    selected_objective,
                    file_content if 'file_content' in locals() and file_content else "",
                    image_parts if 'image_parts' in locals() and is_image else None,
                    50000  # Nuevo límite de caracteres para archivos
                )
                
                if not isinstance(generated_ad, str):
                    st.error("Error al generar el anuncio")
                else:
                    # Store the generated ad in session state to preserve it
                    st.session_state.current_ad = generated_ad
                    
                    # Display the ad
                    st.markdown(f"""
                        <div style="{styles['results_container']}">
                            <h3>Anuncio Generado:</h3>
                            <p>{generated_ad}</p>
                        </div>
                    """, unsafe_allow_html=True)
                    
                    # Aplicar estilo para el botón de descarga
                    st.markdown(styles["download_button"], unsafe_allow_html=True)
                    
                    # Get current timestamp for the filename
                    import datetime
                    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
                    
                    # Botón de descarga with timestamp in filename
                    st.download_button(
                        label="DESCARGAR ANUNCIO",
                        data=generated_ad,
                        file_name=f"anuncio_facebook_{timestamp}.txt",
                        mime="text/plain"
                    )
    else:
        col2.warning("Por favor, completa todos los campos antes de generar el anuncio.")
# If there's a stored ad but no new submission, display it
elif 'current_ad' in st.session_state:
    with col2:
        st.markdown(f"""
            <div style="{styles['results_container']}">
                <h3>Anuncio Generado:</h3>
                <p>{st.session_state.current_ad}</p>
            </div>
        """, unsafe_allow_html=True)
        
        # Aplicar estilo para el botón de descarga
        st.markdown(styles["download_button"], unsafe_allow_html=True)
        
        # Get current timestamp for the filename
        import datetime
        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        
        # Botón de descarga with timestamp in filename
        st.download_button(
            label="DESCARGAR ANUNCIO",
            data=st.session_state.current_ad,
            file_name=f"anuncio_facebook_{timestamp}.txt",
            mime="text/plain"
        )

# Agregar firma del autor al final de la página
st.markdown('---')
st.markdown('Made with ❤️ by Jesús Cabrera')