trying a simple shell script and fixing archives
This commit is contained in:
38
shell/email_processor.awk
Executable file
38
shell/email_processor.awk
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/awk -f
|
||||
|
||||
# Primary email processor using AWK
|
||||
# Lightweight, portable text processing for cleaning up email content
|
||||
|
||||
{
|
||||
# Remove URL defense wrappers step by step
|
||||
gsub(/https:\/\/urldefense\.com\/v3\/__/, "")
|
||||
gsub(/__[^[:space:]]*/, "")
|
||||
|
||||
# Extract and shorten URLs to domains
|
||||
# This processes all URLs in the line
|
||||
while (match($0, /https?:\/\/[^\/[:space:]]+/)) {
|
||||
url = substr($0, RSTART, RLENGTH)
|
||||
# Extract domain (remove protocol)
|
||||
domain = url
|
||||
gsub(/^https?:\/\//, "", domain)
|
||||
# Replace this URL with [domain]
|
||||
sub(url, "[" domain "]", $0)
|
||||
}
|
||||
|
||||
# Remove any remaining URL paths after domain extraction
|
||||
gsub(/\][^[:space:]]*/, "]")
|
||||
|
||||
# Remove mailto links
|
||||
gsub(/mailto:[^[:space:]]*/, "")
|
||||
|
||||
# Clean up email headers - make them bold
|
||||
if (/^From:/) { gsub(/^From:[[:space:]]*/, "**From:** ") }
|
||||
if (/^To:/) { gsub(/^To:[[:space:]]*/, "**To:** ") }
|
||||
if (/^Subject:/) { gsub(/^Subject:[[:space:]]*/, "**Subject:** ") }
|
||||
if (/^Date:/) { gsub(/^Date:[[:space:]]*/, "**Date:** ") }
|
||||
|
||||
# Skip empty lines
|
||||
if (/^[[:space:]]*$/) next
|
||||
|
||||
print
|
||||
}
|
||||
125
shell/email_processor.py
Executable file
125
shell/email_processor.py
Executable file
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Email content processor for run_himalaya.sh
|
||||
Cleans up email content for better readability
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def extract_domain(url):
|
||||
"""Extract domain from URL"""
|
||||
try:
|
||||
parsed = urlparse(url if url.startswith("http") else "http://" + url)
|
||||
return parsed.netloc.replace("www.", "")
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def is_important_url(url, domain):
|
||||
"""Determine if URL should be shown in full"""
|
||||
important_domains = [
|
||||
"github.com",
|
||||
"gitlab.com",
|
||||
"jira.",
|
||||
"confluence.",
|
||||
"docs.google.com",
|
||||
"drive.google.com",
|
||||
"sharepoint.com",
|
||||
"slack.com",
|
||||
"teams.microsoft.com",
|
||||
"zoom.us",
|
||||
]
|
||||
|
||||
# Show short URLs in full
|
||||
if len(url) < 60:
|
||||
return True
|
||||
|
||||
# Show URLs with important domains in shortened form
|
||||
if domain and any(imp in domain for imp in important_domains):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def clean_url_defenses(text):
|
||||
"""Remove URL defense wrappers"""
|
||||
# Remove Proofpoint URL defense
|
||||
text = re.sub(r"https://urldefense\.com/v3/__([^;]+)__;[^$]*\$", r"\1", text)
|
||||
|
||||
# Remove other common URL wrappers
|
||||
text = re.sub(
|
||||
r"https://[^/]*phishalarm[^/]*/[^/]*/[^$]*\$", "[Security Link Removed]", text
|
||||
)
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def process_email_content(content):
|
||||
"""Process email content for better readability"""
|
||||
|
||||
# Remove URL defense wrappers first
|
||||
content = clean_url_defenses(content)
|
||||
|
||||
# Clean up common email artifacts first
|
||||
content = re.sub(
|
||||
r"ZjQcmQRYFpfpt.*?ZjQcmQRYFpfpt\w+End",
|
||||
"[Security Banner Removed]",
|
||||
content,
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
|
||||
# Clean up mailto links that clutter the display
|
||||
content = re.sub(r"mailto:[^\s>]+", "", content)
|
||||
|
||||
# Pattern to match URLs (more conservative)
|
||||
url_pattern = r'https?://[^\s<>"{}|\\^`\[\]\(\)]+[^\s<>"{}|\\^`\[\]\(\).,;:!?]'
|
||||
|
||||
def replace_url(match):
|
||||
url = match.group(0)
|
||||
domain = extract_domain(url)
|
||||
|
||||
if is_important_url(url, domain):
|
||||
if domain and len(url) > 60:
|
||||
return f"[{domain}]"
|
||||
else:
|
||||
return url
|
||||
else:
|
||||
if domain:
|
||||
return f"[{domain}]"
|
||||
else:
|
||||
return "[Link]"
|
||||
|
||||
# Replace URLs
|
||||
content = re.sub(url_pattern, replace_url, content)
|
||||
|
||||
# Clean up email headers formatting
|
||||
content = re.sub(
|
||||
r"^(From|To|Subject|Date):\s*(.+?)$", r"**\1:** \2", content, flags=re.MULTILINE
|
||||
)
|
||||
|
||||
# Clean up angle brackets around email addresses that are left over
|
||||
content = re.sub(r"<[^>]*@[^>]*>", "", content)
|
||||
|
||||
# Remove excessive whitespace but preserve paragraph breaks
|
||||
content = re.sub(r"\n\s*\n\s*\n+", "\n\n", content)
|
||||
content = re.sub(r"[ \t]+", " ", content)
|
||||
|
||||
# Remove lines that are just whitespace
|
||||
content = re.sub(r"^\s*$\n", "", content, flags=re.MULTILINE)
|
||||
|
||||
# Clean up repeated domain references on same line
|
||||
content = re.sub(r"\[([^\]]+)\].*?\[\1\]", r"[\1]", content)
|
||||
|
||||
# Clean up trailing angle brackets and other artifacts
|
||||
content = re.sub(r"[<>]+\s*$", "", content, flags=re.MULTILINE)
|
||||
|
||||
return content.strip()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
content = sys.stdin.read()
|
||||
processed = process_email_content(content)
|
||||
print(processed)
|
||||
262
shell/run_himalaya.sh
Executable file
262
shell/run_himalaya.sh
Executable file
@@ -0,0 +1,262 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enhanced Himalaya Email Management Script
|
||||
# Features: Auto-discovery, smart navigation, streamlined UX
|
||||
|
||||
# Function to get available message IDs from himalaya
|
||||
get_available_message_ids() {
|
||||
himalaya envelope list | awk 'NR > 2 && /^\| [0-9]/ {gsub(/[| ]/, "", $2); if($2 ~ /^[0-9]+$/) print $2}' | sort -n
|
||||
}
|
||||
|
||||
# Function to get the latest (most recent) message ID
|
||||
get_latest_message_id() {
|
||||
get_available_message_ids | tail -1
|
||||
}
|
||||
|
||||
# Function to find the next valid message ID
|
||||
find_next_message_id() {
|
||||
local current_id="$1"
|
||||
get_available_message_ids | awk -v current="$current_id" '$1 > current {print $1; exit}'
|
||||
}
|
||||
|
||||
# Function to find the previous valid message ID
|
||||
find_previous_message_id() {
|
||||
local current_id="$1"
|
||||
get_available_message_ids | awk -v current="$current_id" '$1 < current {prev=$1} END {if(prev) print prev}'
|
||||
}
|
||||
|
||||
# Function to refresh the vim-himalaya buffer
|
||||
refresh_vim_himalaya() {
|
||||
if [ -S "/tmp/nvim-server" ]; then
|
||||
nvim --server /tmp/nvim-server --remote-send "Himalaya<CR>" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to read a single character without waiting for Enter
|
||||
read_char() {
|
||||
stty -echo -icanon time 0 min 1
|
||||
char=$(dd bs=1 count=1 2>/dev/null)
|
||||
stty echo icanon
|
||||
echo "$char"
|
||||
}
|
||||
|
||||
# Function to safely run the himalaya command and handle failures
|
||||
run_himalaya_message_read() {
|
||||
local message_id="$1"
|
||||
local temp_output
|
||||
temp_output=$(himalaya message read "$message_id" 2>&1)
|
||||
local exit_code=$?
|
||||
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
# Choose email processor based on availability
|
||||
local email_processor
|
||||
if [ -f "email_processor.awk" ]; then
|
||||
email_processor="awk -f email_processor.awk"
|
||||
elif command -v python3 >/dev/null 2>&1 && [ -f "email_processor.py" ]; then
|
||||
email_processor="python3 email_processor.py"
|
||||
else
|
||||
email_processor="cat" # fallback to no processing
|
||||
fi
|
||||
|
||||
# Process email content, then render with glow and display with less
|
||||
echo "$temp_output" | \
|
||||
$email_processor | \
|
||||
# bat
|
||||
glow --width 100 -p
|
||||
# less -R -X -F
|
||||
return 0
|
||||
else
|
||||
echo "Failed to open message $message_id."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create a task for the current message
|
||||
create_task_for_message() {
|
||||
local message_id="$1"
|
||||
read -p "Task title: " task_title
|
||||
if [ -n "$task_title" ]; then
|
||||
if command -v task >/dev/null 2>&1; then
|
||||
task add "Followup on email $message_id - $task_title" \
|
||||
--project "Email" \
|
||||
--due "$(date -d '+1 week' +%Y-%m-%d)" \
|
||||
--priority "P3" \
|
||||
--tags "email"
|
||||
echo "Task created for message $message_id."
|
||||
else
|
||||
echo "TaskWarrior not found. Task not created."
|
||||
fi
|
||||
else
|
||||
echo "No task title provided. Task not created."
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to display the action menu and handle user choice
|
||||
show_action_menu() {
|
||||
local current_id="$1"
|
||||
|
||||
# Draw a nice border above the menu
|
||||
echo ""
|
||||
echo "════════════════════════════════════════════════════════════════════════════════"
|
||||
echo " ACTIONS"
|
||||
echo "════════════════════════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo " t) Create task for this message"
|
||||
echo " d) Delete message"
|
||||
echo " a) Archive message"
|
||||
echo " n) Next message"
|
||||
echo " p) Previous message"
|
||||
echo " r) Reopen/redisplay current message"
|
||||
echo " q) Quit"
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────────────────────────────────────────"
|
||||
echo -n "Enter choice: "
|
||||
|
||||
local choice=$(read_char)
|
||||
echo "$choice" # Echo for feedback
|
||||
|
||||
# Clear screen after choice is made
|
||||
clear
|
||||
|
||||
case "$choice" in
|
||||
t|T)
|
||||
create_task_for_message "$current_id"
|
||||
echo "Press any key to continue..."
|
||||
read_char > /dev/null
|
||||
process_message "$current_id"
|
||||
;;
|
||||
d|D)
|
||||
echo "Deleting message $current_id..."
|
||||
if himalaya message delete "$current_id"; then
|
||||
echo "Message $current_id deleted successfully."
|
||||
refresh_vim_himalaya
|
||||
# Try to open next available message
|
||||
local next_id=$(find_next_message_id "$current_id")
|
||||
if [ -n "$next_id" ]; then
|
||||
echo "Opening next message: $next_id"
|
||||
process_message "$next_id"
|
||||
else
|
||||
echo "No more messages. Exiting."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "Failed to delete message $current_id."
|
||||
echo "Press any key to continue..."
|
||||
read_char > /dev/null
|
||||
process_message "$current_id"
|
||||
fi
|
||||
;;
|
||||
a|A)
|
||||
echo "Archiving message $current_id..."
|
||||
if himalaya message move Archives "$current_id"; then
|
||||
echo "Message $current_id archived successfully."
|
||||
refresh_vim_himalaya
|
||||
# Try to open next available message
|
||||
local next_id=$(find_next_message_id "$current_id")
|
||||
if [ -n "$next_id" ]; then
|
||||
echo "Opening next message: $next_id"
|
||||
process_message "$next_id"
|
||||
else
|
||||
echo "No more messages. Exiting."
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
echo "Failed to archive message $current_id."
|
||||
echo "Press any key to continue..."
|
||||
read_char > /dev/null
|
||||
process_message "$current_id"
|
||||
fi
|
||||
;;
|
||||
n|N)
|
||||
local next_id=$(find_next_message_id "$current_id")
|
||||
if [ -n "$next_id" ]; then
|
||||
echo "Opening next message: $next_id"
|
||||
process_message "$next_id"
|
||||
else
|
||||
echo "No next message available."
|
||||
echo "Press any key to continue..."
|
||||
read_char > /dev/null
|
||||
process_message "$current_id"
|
||||
fi
|
||||
;;
|
||||
p|P)
|
||||
local prev_id=$(find_previous_message_id "$current_id")
|
||||
if [ -n "$prev_id" ]; then
|
||||
echo "Opening previous message: $prev_id"
|
||||
process_message "$prev_id"
|
||||
else
|
||||
echo "No previous message available."
|
||||
echo "Press any key to continue..."
|
||||
read_char > /dev/null
|
||||
process_message "$current_id"
|
||||
fi
|
||||
;;
|
||||
r|R)
|
||||
echo "Reopening message $current_id..."
|
||||
process_message "$current_id"
|
||||
;;
|
||||
q|Q)
|
||||
echo "Exiting."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid choice. Please try again."
|
||||
echo "Press any key to continue..."
|
||||
read_char > /dev/null
|
||||
process_message "$current_id"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Function to process a message (read and show menu)
|
||||
process_message() {
|
||||
local message_id="$1"
|
||||
local is_fallback="$2"
|
||||
|
||||
echo "Reading message $message_id..."
|
||||
if run_himalaya_message_read "$message_id"; then
|
||||
show_action_menu "$message_id"
|
||||
else
|
||||
echo "Error reading message $message_id."
|
||||
if [ "$is_fallback" = "true" ]; then
|
||||
# If this was already a fallback attempt, don't try again
|
||||
echo "Unable to read any messages. Exiting."
|
||||
exit 1
|
||||
else
|
||||
# Try to find a valid message to fall back to
|
||||
echo "Trying to find an available message..."
|
||||
local latest_id=$(get_latest_message_id)
|
||||
if [ -n "$latest_id" ] && [ "$latest_id" != "$message_id" ]; then
|
||||
echo "Opening latest message: $latest_id"
|
||||
process_message "$latest_id" "true"
|
||||
else
|
||||
echo "No valid messages found. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Main script logic
|
||||
|
||||
# Check if message ID is provided, otherwise get the latest
|
||||
if [ -z "$1" ]; then
|
||||
echo "No message ID provided. Finding latest message..."
|
||||
message_id=$(get_latest_message_id)
|
||||
if [ -z "$message_id" ]; then
|
||||
echo "No messages found in inbox."
|
||||
exit 1
|
||||
fi
|
||||
echo "Latest message ID: $message_id"
|
||||
else
|
||||
message_id="$1"
|
||||
fi
|
||||
|
||||
# Validate that himalaya is available
|
||||
if ! command -v himalaya >/dev/null 2>&1; then
|
||||
echo "Error: himalaya command not found. Please install himalaya."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start processing the message
|
||||
process_message "$message_id"
|
||||
41
shell/run_tests.sh
Executable file
41
shell/run_tests.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test runner for run_himalaya.sh
|
||||
|
||||
echo "===================="
|
||||
echo "Himalaya Script Test Suite"
|
||||
echo "===================="
|
||||
echo
|
||||
|
||||
# Check if glow is available (needed for message display)
|
||||
if ! command -v glow >/dev/null 2>&1; then
|
||||
echo "Warning: glow not found. Some tests may behave differently."
|
||||
echo "Install with: brew install glow"
|
||||
echo
|
||||
fi
|
||||
|
||||
# Get the script directory
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Set up PATH to include our test mocks
|
||||
export PATH="$SCRIPT_DIR/tests:$PATH"
|
||||
|
||||
# Run unit tests
|
||||
echo "=== Unit Tests ==="
|
||||
if ! bash "$SCRIPT_DIR/tests/unit_tests.sh"; then
|
||||
echo "Unit tests failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Integration Tests ==="
|
||||
if ! bash "$SCRIPT_DIR/tests/integration_tests.sh"; then
|
||||
echo "Integration tests failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "===================="
|
||||
echo "All tests passed! ✅"
|
||||
echo "===================="
|
||||
8
shell/test_refactored.sh
Executable file
8
shell/test_refactored.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Test script for the refactored code
|
||||
|
||||
echo "Testing the refactored code with a dry run (no attachment download)..."
|
||||
python3 main.py sync --dry-run
|
||||
|
||||
echo -e "\nTesting with attachment downloading enabled..."
|
||||
python3 main.py sync --dry-run --download-attachments
|
||||
Reference in New Issue
Block a user