|
|
|
import os, random, sqlite3, tempfile, zipfile, re
|
|
from typing import Tuple
|
|
|
|
import gradio as gr
|
|
from PIL import Image, ImageDraw, ImageFont
|
|
from gtts import gTTS
|
|
import speech_recognition as sr
|
|
from pydub import AudioSegment
|
|
from transformers import pipeline
|
|
|
|
|
|
OUTPUT_DIR = "outputs"
|
|
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
|
|
DB_PATH = "excuses.db"
|
|
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
|
|
cur = conn.cursor()
|
|
cur.execute("CREATE TABLE IF NOT EXISTS history (id INTEGER PRIMARY KEY, excuse TEXT)")
|
|
conn.commit()
|
|
|
|
|
|
SCENARIO_EXCUSES = {
|
|
"being late": [
|
|
"I got stuck in unexpected traffic due to an accident ahead.",
|
|
"My alarm didn’t go off because of a power outage last night.",
|
|
"There was a medical emergency in my building."
|
|
],
|
|
"missing class": [
|
|
"I had a severe migraine and couldn’t focus.",
|
|
"I had to attend a sudden family emergency.",
|
|
"My transport broke down on the way to campus."
|
|
],
|
|
"missing work": [
|
|
"I woke up with a stomach infection and couldn’t get out of bed.",
|
|
"I had to rush a neighbor to the hospital early morning.",
|
|
"My internet went down right before my shift."
|
|
],
|
|
"forgetting homework": [
|
|
"I was caring for a sick family member all night.",
|
|
"My laptop crashed and the file got corrupted.",
|
|
"I misread the deadline and thought it was tomorrow."
|
|
],
|
|
"missing meeting": [
|
|
"I lost track of time with back-to-back tasks.",
|
|
"I had a sudden network issue right before it started.",
|
|
"I got pulled into an unavoidable client call."
|
|
],
|
|
"not answering call": [
|
|
"My phone was on silent while I was in a session.",
|
|
"I was driving and couldn’t pick up safely.",
|
|
"I was handling a small emergency and missed it."
|
|
],
|
|
}
|
|
|
|
def generate_excuse(scenario: str) -> str:
|
|
return random.choice(SCENARIO_EXCUSES.get(
|
|
scenario, ["I’m sorry, I don’t have an excuse for that scenario."]
|
|
))
|
|
|
|
|
|
def _ensure_wav(path: str) -> str:
|
|
ext = os.path.splitext(path)[1].lower()
|
|
if ext in [".wav", ".aif", ".aiff", ".flac"]:
|
|
return path
|
|
wav_path = os.path.join(tempfile.gettempdir(), "sr_input.wav")
|
|
AudioSegment.from_file(path).export(wav_path, format="wav")
|
|
return wav_path
|
|
|
|
def voice_to_text(audio_path: str) -> str:
|
|
if not audio_path:
|
|
return "No audio file provided."
|
|
try:
|
|
r = sr.Recognizer()
|
|
wav_path = _ensure_wav(audio_path)
|
|
with sr.AudioFile(wav_path) as src:
|
|
audio = r.record(src)
|
|
return r.recognize_google(audio)
|
|
except Exception as e:
|
|
return f"Transcription failed: {e}"
|
|
|
|
|
|
def generate_fake_proof(excuse_text: str) -> Tuple[str, str]:
|
|
if not excuse_text.strip():
|
|
excuse_text = "Sorry, I had an unavoidable emergency."
|
|
|
|
width, height = 800, 380
|
|
img = Image.new("RGB", (width, height), "#ffffff")
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
try:
|
|
font = ImageFont.truetype("arial.ttf", 22)
|
|
font_bold = ImageFont.truetype("arialbd.ttf", 24)
|
|
except:
|
|
font = ImageFont.load_default()
|
|
font_bold = font
|
|
|
|
draw.rectangle((0, 0, width, 54), fill="#f0f0f0")
|
|
draw.text((16, 14), "Chat with Boss", fill="#333333", font=font_bold)
|
|
|
|
draw.rectangle((20, 80, width - 20, 140), outline="#dddddd", fill="#f8f8f8")
|
|
draw.text((28, 92), "Boss: Why didn’t you show up today?", fill="#111111", font=font)
|
|
|
|
you_box_top = 160
|
|
draw.rectangle((20, you_box_top, width - 20, you_box_top + 120), outline="#cfe2ff", fill="#eaf2ff")
|
|
|
|
words = excuse_text.strip()
|
|
lines, line = [], ""
|
|
for token in words.split():
|
|
if len(line + " " + token) < 60:
|
|
line = (line + " " + token).strip()
|
|
else:
|
|
lines.append(line); line = token
|
|
if line: lines.append(line)
|
|
|
|
y = you_box_top + 16
|
|
draw.text((28, y), "You:", fill="#114488", font=font_bold)
|
|
y += 30
|
|
for ln in lines:
|
|
draw.text((40, y), ln, fill="#114488", font=font); y += 26
|
|
|
|
draw.rectangle((20, 300, width - 20, 352), outline="#dddddd", fill="#f8f8f8")
|
|
draw.text((28, 314), "Boss: Okay, take care.", fill="#111111", font=font)
|
|
|
|
file_path = os.path.join(OUTPUT_DIR, "chat_proof.png")
|
|
img.save(file_path)
|
|
return file_path, file_path
|
|
|
|
|
|
def fake_emergency():
|
|
return "🚨 Emergency alert sent to: Mom, Boss, and HR."
|
|
|
|
|
|
apology_gen = pipeline("text-generation", model="gpt2")
|
|
def generate_apology(excuse_text: str) -> str:
|
|
prompt = f"Write a short, sincere apology message for: {excuse_text}\nApology:"
|
|
out = apology_gen(
|
|
prompt,
|
|
max_new_tokens=60,
|
|
num_return_sequences=1,
|
|
do_sample=True,
|
|
temperature=0.8,
|
|
pad_token_id=50256
|
|
)[0]["generated_text"]
|
|
m = re.split(r"Apology:\s*", out, flags=re.IGNORECASE)
|
|
return m[-1].strip()[:400]
|
|
|
|
|
|
def generate_voice(excuse_text: str) -> Tuple[str, str]:
|
|
tt = excuse_text.strip() or "I’m sorry, I had a genuine emergency."
|
|
path = os.path.join(OUTPUT_DIR, "excuse.mp3")
|
|
gTTS(text=tt).save(path)
|
|
return path, path
|
|
|
|
|
|
def save_to_history(excuse_text: str) -> str:
|
|
try:
|
|
if excuse_text.strip():
|
|
cur.execute("INSERT INTO history (excuse) VALUES (?)", (excuse_text.strip(),))
|
|
conn.commit()
|
|
return "✅ Saved to history."
|
|
return "⚠ Nothing to save."
|
|
except Exception as e:
|
|
return f"❌ Failed to save: {e}"
|
|
|
|
def get_history() -> str:
|
|
cur.execute("SELECT excuse FROM history ORDER BY id DESC LIMIT 8")
|
|
rows = cur.fetchall()
|
|
return "\n".join(f"• {r[0]}" for r in rows) or "No history yet."
|
|
|
|
|
|
def zip_outputs() -> str:
|
|
zip_path = "outputs.zip"
|
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
|
for f in os.listdir(OUTPUT_DIR):
|
|
z.write(os.path.join(OUTPUT_DIR, f), arcname=f)
|
|
return zip_path
|
|
|
|
|
|
with gr.Blocks(title="Intelligent Excuse Generator") as app:
|
|
gr.Markdown("## 🚀 Intelligent Excuse Generator")
|
|
|
|
with gr.Row():
|
|
scenario = gr.Dropdown(
|
|
label="Choose Scenario",
|
|
choices=list(SCENARIO_EXCUSES.keys()),
|
|
value="being late"
|
|
)
|
|
gen_btn = gr.Button("🎲 Generate Excuse", variant="primary")
|
|
excuse_box = gr.Textbox(label="Generated Excuse", lines=3)
|
|
gen_btn.click(generate_excuse, inputs=scenario, outputs=excuse_box)
|
|
|
|
with gr.Row():
|
|
audio_in = gr.Audio(label="🎤 Upload Audio (wav/mp3)", sources=["upload"], type="filepath")
|
|
trans_btn = gr.Button("📝 Convert Voice → Text")
|
|
transcript = gr.Textbox(label="Transcript", lines=3)
|
|
trans_btn.click(voice_to_text, inputs=audio_in, outputs=transcript)
|
|
|
|
with gr.Row():
|
|
proof_btn = gr.Button("📸 Generate Fake Proof")
|
|
proof_img = gr.Image(label="Preview: Chat Screenshot", type="filepath")
|
|
proof_file = gr.File(label="Download: chat_proof.png")
|
|
proof_btn.click(generate_fake_proof, inputs=excuse_box, outputs=[proof_img, proof_file])
|
|
|
|
with gr.Row():
|
|
apology_btn = gr.Button("🙏 Generate Apology")
|
|
apology_out = gr.Textbox(label="Apology", lines=5)
|
|
apology_btn.click(generate_apology, inputs=excuse_box, outputs=apology_out)
|
|
|
|
with gr.Row():
|
|
voice_btn = gr.Button("🔊 Create Voice Excuse (mp3)")
|
|
voice_player = gr.Audio(label="Play Excuse", type="filepath")
|
|
voice_file = gr.File(label="Download: excuse.mp3")
|
|
voice_btn.click(generate_voice, inputs=excuse_box, outputs=[voice_player, voice_file])
|
|
|
|
with gr.Row():
|
|
save_btn = gr.Button("💾 Save Excuse to History")
|
|
load_btn = gr.Button("📂 Load History")
|
|
status = gr.Markdown()
|
|
history_box = gr.Textbox(label="Recent Saved Excuses", lines=6)
|
|
save_btn.click(save_to_history, inputs=excuse_box, outputs=status)
|
|
load_btn.click(get_history, outputs=history_box)
|
|
|
|
with gr.Row():
|
|
zip_btn = gr.Button("⬇ Zip & Download All Outputs")
|
|
zip_file = gr.File(label="outputs.zip")
|
|
zip_btn.click(zip_outputs, outputs=zip_file)
|
|
|
|
app.launch(share=True) |