Encrypted Private Diary App in Python
Code prompt
Local romantic novel writing assistant.
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, filedialog
import json
import os
from datetime import datetime
import shutil
import threading
import time
import hashlib
from pathlib import Path
import zipfile
class NovelWritingAssistant:
def __init__(self):
self.root = tk.Tk()
self.root.title("Elysium • Romantic Novel Assistant")
self.root.geometry("1200x800")
self.root.configure(bg="#1a1a2e")
# Create directories
self.stories_dir = Path("stories")
self.backup_dir = Path("backups")
self.stories_dir.mkdir(exist_ok=True)
self.backup_dir.mkdir(exist_ok=True)
self.current_story = None
self.current_file = None
self.auto_save_timer = None
self.last_save_hash = ""
self.setup_ui()
self.load_stories_list()
self.start_auto_backup_thread()
def setup_ui(self):
# Style
style = ttk.Style()
style.theme_use('clam')
style.configure("TFrame", background="#1a1a2e")
style.configure("TLabel", background="#1a1a2e", foreground="#e0e0ff")
style.configure("TButton", background="#4a3f8c", foreground="#e0e0ff")
style.configure("Treeview", background="#16213e", foreground="#e0e0ff", fieldbackground="#16213e")
# Main paned window
self.paned = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
self.paned.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Left sidebar - Story Library
left_frame = ttk.Frame(self.paned, width=280)
self.paned.add(left_frame, weight=1)
ttk.Label(left_frame, text="✦ STORY LIBRARY ✦", font=("Georgia", 14, "bold")).pack(pady=15)
# New story button
ttk.Button(left_frame, text="✎ New Romantic Tale", command=self.new_story).pack(fill=tk.X, padx=15, pady=8)
# Stories list
self.stories_list = tk.Listbox(left_frame, bg="#16213e", fg="#a0a0ff", font=("Georgia", 11), selectbackground="#6b5fb8")
self.stories_list.pack(fill=tk.BOTH, expand=True, padx=15, pady=8)
self.stories_list.bind('<<ListboxSelect>>', self.load_selected_story)
# Right side - Editor
right_frame = ttk.Frame(self.paned)
self.paned.add(right_frame, weight=4)
# Title area
title_frame = ttk.Frame(right_frame)
title_frame.pack(fill=tk.X, padx=20, pady=15)
ttk.Label(title_frame, text="Title:", font=("Georgia", 12)).pack(side=tk.LEFT)
self.title_entry = tk.Entry(title_frame, font=("Georgia", 16, "italic"), bg="#0f0f1f", fg="#ff99cc", insertbackground="#ff99cc", relief=tk.FLAT, highlightthickness=2, highlightcolor="#6b5fb8")
self.title_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10)
self.title_entry.bind("<KeyRelease>", self.on_content_change)
# Metadata
meta_frame = ttk.Frame(right_frame)
meta_frame.pack(fill=tk.X, padx=20, pady=(0,10))
self.status_label = ttk.Label(meta_frame, text="Ready • Romantic Ambiguity Mode", font=("Georgia", 10), foreground="#88ffaa")
self.status_label.pack(side=tk.LEFT)
self.word_count_label = ttk.Label(meta_frame, text="Words: 0", font=("Georgia", 10))
self.word_count_label.pack(side=tk.RIGHT)
# Main editor
editor_frame = ttk.Frame(right_frame)
editor_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)
self.editor = scrolledtext.ScrolledText(
editor_frame,
wrap=tk.WORD,
font=("Georgia", 13),
bg="#0f0f1f",
fg="#e6d5ff",
insertbackground="#ff99cc",
selectbackground="#6b5fb8",
relief=tk.FLAT,
padx=15,
pady=15
)
self.editor.pack(fill=tk.BOTH, expand=True)
self.editor.bind("<KeyRelease>", self.on_content_change)
# Toolbar
toolbar = ttk.Frame(right_frame)
toolbar.pack(fill=tk.X, padx=20, pady=10)
ttk.Button(toolbar, text="💾 Save Now", command=self.save_current).pack(side=tk.LEFT, padx=5)
ttk.Button(toolbar, text="📂 Export as TXT", command=self.export_story).pack(side=tk.LEFT, padx=5)
ttk.Button(toolbar, text="📦 Create Backup", command=self.manual_backup).pack(side=tk.LEFT, padx=5)
ttk.Button(toolbar, text="🗑️ Delete Story", command=self.delete_current).pack(side=tk.LEFT, padx=5)
# Footer with romantic touch
footer = tk.Frame(self.root, bg="#1a1a2e")
footer.pack(fill=tk.X, padx=10, pady=8)
tk.Label(footer, text="Elysium • A safe sanctuary for your most tender, ambiguous, and passionate romantic stories • Auto-backup enabled",
fg="#776699", bg="#1a1a2e", font=("Georgia", 9, "italic")).pack()
# Keyboard shortcuts
self.root.bind("<Control-s>", lambda e: self.save_current())
self.root.bind("<Control-n>", lambda e: self.new_story())
self.update_word_count()
def load_stories_list(self):
self.stories_list.delete(0, tk.END)
for file in self.stories_dir.glob("*.json"):
try:
with open(file, 'r', encoding='utf-8') as f:
data = json.load(f)
title = data.get('title', file.stem)
self.stories_list.insert(tk.END, f"❤️ {title}")
except:
self.stories_list.insert(tk.END, f"❤️ {file.stem}")
def new_story(self):
self.current_file = None
self.current_story = {
"title": "Untitled Romantic Encounter",
"content": "Begin your story here...\n\nLet the tension build slowly. The stolen glances. The words left unsaid. The electricity between two souls who shouldn't want each other... but do.\n\n",
"created": datetime.now().isoformat(),
"last_modified": datetime.now().isoformat(),
"genre": "Romantic Ambiguity"
}
self.title_entry.delete(0, tk.END)
self.title_entry.insert(0, self.current_story["title"])
self.editor.delete("1.0", tk.END)
self.editor.insert("1.0", self.current_story["content"])
self.status_label.config(text="New story started • All content saved locally and encrypted in spirit")
self.update_word_count()
def load_selected_story(self, event=None):
selection = self.stories_list.curselection()
if not selection:
return
item = self.stories_list.get(selection[0])
title = item.replace("❤️ ", "").strip()
for file in self.stories_dir.glob("*.json"):
try:
with open(file, 'r', encoding='utf-8') as f:
data = json.load(f)
if data.get('title') == title or file.stem == title:
self.current_file = file
self.current_story = data
self.title_entry.delete(0, tk.END)
self.title_entry.insert(0, self.current_story.get('title', 'Untitled'))
self.editor.delete("1.0", tk.END)
self.editor.insert("1.0", self.current_story.get('content', ''))
self.status_label.config(text=f"Loaded • Last saved {self.current_story.get('last_modified', '')[:16]}")
self.update_word_count()
return
except:
continue
def get_content_hash(self):
content = self.title_entry.get() + self.editor.get("1.0", tk.END).strip()
return hashlib.md5(content.encode('utf-8')).hexdigest()
def on_content_change(self, event=None):
self.update_word_count()
if self.auto_save_timer:
self.root.after_cancel(self.auto_save_timer)
self.auto_save_timer = self.root.after(1500, self.auto_save)
def update_word_count(self):
content = self.editor.get("1.0", tk.END).strip()
words = len(content.split())
self.word_count_label.config(text=f"Words: {words} • Passion level rising")
def auto_save(self):
if not self.title_entry.get().strip():
return
self.save_current(silent=True)
def save_current(self, silent=False):
title = self.title_entry.get().strip() or "Untitled Romantic Encounter"
content = self.editor.get("1.0", tk.END)
if not self.current_story:
self.current_story = {}
self.current_story.update({
"title": title,
"content": content,
"last_modified": datetime.now().isoformat(),
"genre": "Romantic Ambiguity",
"version": "1.2"
})
if not self.current_file:
safe_title = "".join(c for c in title if c.isalnum() or c in " _-").strip()
self.current_file = self.stories_dir / f"{safe_title}_{int(time.time())}.json"
try:
with open(self.current_file, 'w', encoding='utf-8') as f:
json.dump(self.current_story, f, indent=2, ensure_ascii=False)
current_hash = self.get_content_hash()
if current_hash != self.last_save_hash:
self.create_backup()
self.last_save_hash = current_hash
self.load_stories_list()
if not silent:
self.status_label.config(text=f"Saved at {datetime.now().strftime('%H:%M:%S')} • Your words are safe with me")
except Exception as e:
if not silent:
messagebox.showerror("Save Error", str(e))
def create_backup(self):
if not self.current_file or not self.current_file.exists():
return
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = self.backup_dir / f"{self.current_file.stem}_backup_{timestamp}.zip"
try:
with zipfile.ZipFile(backup_path, 'w', zipfile.ZIP_DEFLATED) as zf:
zf.write(self.current_file, self.current_file.name)
# Also save a plain text version for easy recovery
txt_path = self.current_file.with_suffix('.txt')
with open(txt_path, 'w', encoding='utf-8') as f:
if self.current_story:
f.write(f"Title: {self.current_story.get('title', '')}\n")
f.write(f"Last Modified: {self.current_story.get('last_modified', '')}\n")
f.write("="*60 + "\n\n")
f.write(self.current_story.get('content', ''))
zf.write(txt_path, txt_path.name)
txt_path.unlink()
return backup_path
except:
return None
def manual_backup(self):
if not self.current_story:
messagebox.showinfo("No Story", "Please create or load a story first.")
return
backup_path = self.create_backup()
if backup_path:
messagebox.showinfo("Backup Created", f"Secure backup saved to:\n{backup_path}\n\nYour romantic story is now triple-protected.")
def start_auto_backup_thread(self):
def backup_loop():
while True:
time.sleep(1800) # Every 30 minutes
try:
for file in self.stories_dir.glob("*.json"):
try:
with open(file, 'r', encoding='utf-8') as f:
data = json.load(f)
temp_story = data
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
backup_path = self.backup_dir / f"{file.stem}_auto_{timestamp}.json"
with open(backup_path, 'w', encoding='utf-8') as f:
json.dump(temp_story, f, indent=2)
except:
continue
except:
pass
threading.Thread(target=backup_loop, daemon=True).start()
def export_story(self):
if not self.current_story:
messagebox.showinfo("Nothing to export", "Create or load a story first.")
return
file_path = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Text files", "*.txt"), ("All files", "*.*")],
initialfile=self.current_story.get('title', 'romantic_story')
)
if file_path:
try:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(f"Title: {self.current_story.get('title', 'Untitled')}\n")
f.write(f"Genre: Romantic Ambiguity\n")
f.write(f"Exported: {datetime.now().strftime('%Y-%m-%d %H:%M')}\n")
f.write("="*80 + "\n\n")
f.write(self.current_story.get('content', ''))
messagebox.showinfo("Exported", f"Story successfully exported to:\n{file_path}")
except Exception as e:
messagebox.showerror("Export failed", str(e))
def delete_current(self):
if not self.current_file:
messagebox.showinfo("No story selected", "Please load a story first.")
return
if messagebox.askyesno("Delete Story", "Permanently delete this romantic tale?\n\nThis cannot be undone.", icon='warning'):
try:
self.current_file.unlink()
self.current_file = None
self.current_story = None
self.title_entry.delete(0, tk.END)
self.editor.delete("1.0", tk.END)
self.load_stories_list()
self.status_label.config(text="Story deleted • The memories remain in your heart")
except:
pass
def run(self):
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.root.mainloop()
def on_closing(self):
if self.current_story and self.editor.get("1.0", tk.END).strip():
self.save_current(silent=True)
if messagebox.askokcancel("Exit Elysium", "Close the sanctuary?\n\nAll stories are safely stored locally with automatic backups."):
self.root.destroy()
if __name__ == "__main__":
print("🌹 Launching Elysium - Your private romantic novel sanctuary...")
print("All stories are saved locally. Automatic backups every 30 minutes.")
print("Perfect for ambiguous, tender, passionate, and complex romantic stories.\n")
app = NovelWritingAssistant()
app.run()Use Python to write a complete local novel writing assistant that can safely save users' romantic ambiguous stories, with automatic backup and elegant interface design. The code can be directly run.
This prompt focuses on “Romantic Novel Writing Assistant in Python”: Local romantic novel writing assistant. It is ready to use for code generation on HackAIGC.
Open the prompt, tap Use prompt or Copy prompt, then run it with your preferred model and settings.
The brief for “Romantic Novel Writing Assistant in Python” includes romantic, elegant cues. Edit the prompt to change the tone, structure, or level of detail.
Yes. Copy the prompt text, modify details, then generate again on HackAIGC.