import os import random import json import openai import streamlit as st from dotenv import load_dotenv from st_copy import copy_button from cryptography.fernet import Fernet # Load environment variables from .env file load_dotenv() # Set page config - MUST BE THE FIRST STREAMLIT COMMAND st.set_page_config( page_title="Petition Email Generator", page_icon="📧", layout="wide", ) # Get the password token from environment variables PASSWORD_TOKEN = os.environ.get("PASSWORD_TOKEN") # Function to load encrypted email examples def load_email_examples(): try: # Get the encryption key from environment variables encryption_key = os.environ.get("ENCRYPTION_KEY") if not encryption_key: st.warning("Encryption key not found in environment variables.") return None # Simple path to the encrypted file file_path = "examples/encrypted_examples.bin" # Check if the file exists if not os.path.exists(file_path): st.warning(f"Encrypted examples file not found at {file_path}") return None # Read the encrypted file with open(file_path, "rb") as file: encrypted_data = file.read() # Decrypt the data fernet = Fernet(encryption_key) decrypted_data = fernet.decrypt(encrypted_data).decode() # Parse the JSON data examples_data = json.loads(decrypted_data) return examples_data except Exception as e: st.error(f"Error loading examples: {str(e)}") return None # Initialize authentication state if 'is_authenticated' not in st.session_state: st.session_state.is_authenticated = False # If not authenticated, show login form if not st.session_state.is_authenticated: st.title("Petition Email Generator") st.subheader("Authentication Required") with st.form("auth_form"): password = st.text_input("Enter password", type="password") submitted = st.form_submit_button("Login") if submitted: if password == PASSWORD_TOKEN: st.session_state.is_authenticated = True st.rerun() # Refresh the page after successful login else: st.error("Incorrect password") else: # Title and description st.title("Petition Email Generator") st.markdown(""" This app helps you create a personalized email to support a Petition. Follow the steps below to generate your unique email. """) # Tips section moved from footer to top st.markdown(""" ### Tips for Effective Emails - Be polite and respectful - Clearly state your support for the petition - Share personal experiences if relevant - Thank the recipient for their time and consideration """) st.markdown("---") # Initialize session state for storing the generated email if 'generated_email' not in st.session_state: st.session_state.generated_email = "" if 'example_generated' not in st.session_state: st.session_state.example_generated = False if 'generated_subject' not in st.session_state: st.session_state.generated_subject = "" # Only access environment variables if authenticated if st.session_state.is_authenticated: # Set API key from environment variable openai.api_key = os.environ.get("OPENAI_API_KEY") # Load email examples email_examples = load_email_examples() # Fallback to environment variable if encryption doesn't work example_email = os.environ.get("EXAMPLE_EMAIL") # Get recipient email addresses to_emails = os.environ.get("TO_EMAILS", "") else: # If not authenticated, don't set API key openai.api_key = None email_examples = None example_email = "Authentication required to access the example email template." to_emails = "" # Function to generate email with subject (only works when authenticated) def generate_email(name, location, feedback="", model=os.environ.get("MODEL", "gpt-4.1-nano")): # Check authentication if not st.session_state.is_authenticated: st.error("Authentication required. Please use the correct password in the URL.") return None, None try: # If we have loaded examples, use them for a more varied prompt if email_examples: # Either pick a random full example email if random.random() < 0.3: # 30% chance to use a complete example selected_example = random.choice(email_examples["example_emails"]) example_body = selected_example["full_email"] # Or compose a new one from modular blocks else: blocks = email_examples["modular_blocks"] selected_blocks = { category: random.choice(blocks[category]) for category in blocks.keys() } # Build a coherent email from the selected blocks example_body = "\n\n".join([ selected_blocks["intro"], selected_blocks["concern"], selected_blocks["argument"], selected_blocks["support"], selected_blocks["closing"] ]) # Use the constructed example base_example = example_body else: # Fallback to the environment variable example base_example = example_email print("BASE_EXAMPLE:") print(base_example) prompt = f""" Generate a personalized and unique email supporting the Petition based on this example: ALWAYS start the email with "Dear Commissioners" {base_example} Customize it for: - Name of sender: {name} - Location of sender: {location} Additional preferences: {feedback if feedback else "None provided."} Return BOTH: 1. A concise, compelling subject line (one line only) 2. The email body text Format your response exactly as follows: SUBJECT: [Your generated subject line here] [Your generated email body here] Make sure the email is unique, professional, and persuasive. """ response = openai.chat.completions.create( model=model, messages=[ {"role": "system", "content": "You are a helpful assistant that creates personalized emails."}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=800 ) content = response.choices[0].message.content # Extract subject and body from the response if "SUBJECT:" in content: parts = content.split("\n\n", 1) subject = parts[0].replace("SUBJECT:", "").strip() body = parts[1] if len(parts) > 1 else "" return subject, body else: # Fallback if format wasn't followed return "Support for the Petition", content except Exception as e: st.error(f"Error generating email: {str(e)}") return None, None # Main content with two columns col1, col2 = st.columns([1, 1]) with col1: st.subheader("📝 Steps to Generate Your Email") # Step 1: Enter Your Information info_step = st.expander("Step 1: Enter Your Information", expanded=True) with info_step: user_name = st.text_input("Your Name") location = st.text_input("Your City or County") if user_name and location: st.success("Information provided! ✅") # Step 2: Additional Preferences (Optional) pref_step = st.expander("Step 2: Customize Your Email (Optional)", expanded=True) with pref_step: additional_feedback = st.text_area( "Additional details or preferences for your email", placeholder="E.g., I'd like to emphasize economic benefits, include personal stories, etc." ) if additional_feedback: st.success("Customization added! ✅") # Step 3: Generate Email generate_step = st.expander("Step 3: Generate Your Email", expanded=True) with generate_step: # Disable button if not authenticated or missing required fields generate_button = st.button( "Generate Email", type="primary", disabled=(not st.session_state.is_authenticated) or (not user_name or not location) ) if not st.session_state.is_authenticated: st.warning("Authentication required to generate emails.") elif not user_name or not location: st.warning("Please complete Step 1 (Your Information) first.") with col2: st.subheader("📨 Your Personalized Email") # Only attempt to pre-generate example if authenticated if st.session_state.is_authenticated and not st.session_state.example_generated and not generate_button: with st.spinner("Generating an example email..."): example_subject, example_body = generate_email("Jane Smith", "Springfield County") if example_body: st.session_state.generated_subject = example_subject st.session_state.generated_email = example_body st.session_state.example_generated = True st.info("Here's an example of a generated email. Customize it by entering your information and preferences in the steps on the left.") elif not st.session_state.is_authenticated: st.info("Authentication required to generate example emails.") # Handle manual generation if generate_button and user_name and location: with st.spinner("Generating your personalized email..."): generated_subject, generated_body = generate_email(user_name, location, additional_feedback) if generated_body: st.session_state.generated_subject = generated_subject st.session_state.generated_email = generated_body st.success("Your personalized email has been generated! ✅") # Display recipient email addresses if available if to_emails: st.subheader("📧 Send your email to:") email_col, copy_email_col = st.columns([9, 1]) with email_col: st.code(to_emails, language=None) with copy_email_col: st.markdown('
', unsafe_allow_html=True) copy_button(to_emails) st.markdown('
', unsafe_allow_html=True) # Display the subject line (if available) if st.session_state.generated_subject: subject_container = st.container() with subject_container: st.subheader("Subject Line:") subject_col, copy_subject_col = st.columns([9, 1]) with subject_col: subject_text = st.text_input( "", value=st.session_state.generated_subject, label_visibility="collapsed" ) with copy_subject_col: copy_button(subject_text) # Display the email body (either pre-generated or manually generated) email_container = st.container() with email_container: st.subheader("Email Body:") body_col, copy_body_col = st.columns([9, 1]) with body_col: email_text = st.text_area( "", value=st.session_state.generated_email, height=350, label_visibility="collapsed" ) with copy_body_col: copy_button(st.session_state.generated_email)