Tag: #Automation

  • AES File Encryption & Decryption Utility Python Script

    A Python utility that allows users to securely encrypt or decrypt files using AES with a choice between 128‑bit and 256‑bit keys. It lists files from the current directory, prompts for password confirmation during encryption, and handles decryption seamlessly by deriving keys with PBKDF2 and using CBC mode.

    This script relies on the PyCryptodome library to handle AES encryption and decryption operations. Be sure to install it via pip install pycryptodome before running the script.

    Click to view script…
    #!/usr/bin/env python3
    import os
    import sys
    import getpass
    from Crypto.Cipher import AES
    from Crypto.Random import get_random_bytes
    from Crypto.Protocol.KDF import PBKDF2
    from Crypto.Util.Padding import pad, unpad
    
    HEADER_MAGIC = b"ENCR"  # 4 bytes header to identify our encrypted files
    SALT_SIZE = 16          # Salt size in bytes
    IV_SIZE = 16            # AES block size
    PBKDF2_ITERATIONS = 100000
    
    def list_files():
        files = [f for f in os.listdir('.') if os.path.isfile(f)]
        if not files:
            print("No files found in the current directory.")
            sys.exit(1)
        print("Files in the current directory:")
        for i, f in enumerate(files, start=1):
            print(f"  {i}. {f}")
        return files
    
    def choose_file(files):
        try:
            choice = int(input("Select a file number: "))
            if choice < 1 or choice > len(files):
                raise ValueError
        except ValueError:
            print("Invalid selection.")
            sys.exit(1)
        return files[choice - 1]
    
    def get_password(confirm=False):
        pwd = getpass.getpass("Enter password: ")
        if confirm:
            pwd_confirm = getpass.getpass("Confirm password: ")
            if pwd != pwd_confirm:
                print("Passwords do not match.")
                sys.exit(1)
        return pwd.encode()  # work with bytes
    
    def encrypt_file(filepath):
        print("\n--- Encryption ---")
        # Choose key size
        key_size_choice = input("Choose key size (enter 128 or 256): ").strip()
        if key_size_choice not in ("128", "256"):
            print("Invalid key size selection.")
            sys.exit(1)
        key_len = 16 if key_size_choice == "128" else 32
    
        password = get_password(confirm=True)
    
        # Generate a random salt and IV
        salt = get_random_bytes(SALT_SIZE)
        iv = get_random_bytes(IV_SIZE)
    
        # Derive the AES key from the password and salt
        key = PBKDF2(password, salt, dkLen=key_len, count=PBKDF2_ITERATIONS)
    
        # Read file data
        try:
            with open(filepath, "rb") as f:
                data = f.read()
        except IOError as e:
            print(f"Error reading file: {e}")
            sys.exit(1)
    
        # Pad and encrypt the data using CBC mode
        cipher = AES.new(key, AES.MODE_CBC, iv)
        ciphertext = cipher.encrypt(pad(data, AES.block_size))
    
        # Create an output file format:
        # [4 bytes header][1 byte key length indicator][16 bytes salt][16 bytes IV][ciphertext]
        # We store key length as one byte: 16 (for 128-bit) or 32 (for 256-bit)
        header = HEADER_MAGIC + bytes([key_len]) + salt + iv
        out_data = header + ciphertext
    
        # Determine output file name
        out_filepath = filepath + ".enc"
        try:
            with open(out_filepath, "wb") as f:
                f.write(out_data)
        except IOError as e:
            print(f"Error writing encrypted file: {e}")
            sys.exit(1)
    
        print(f"Encryption successful. Encrypted file: {out_filepath}")
    
    def decrypt_file(filepath):
        print("\n--- Decryption ---")
        # Read the encrypted file and parse header information
        try:
            with open(filepath, "rb") as f:
                file_data = f.read()
        except IOError as e:
            print(f"Error reading file: {e}")
            sys.exit(1)
    
        # Verify header length
        header_length = len(HEADER_MAGIC) + 1 + SALT_SIZE + IV_SIZE
        if len(file_data) < header_length:
            print("File is too short to be a valid encrypted file.")
            sys.exit(1)
    
        header = file_data[:len(HEADER_MAGIC)]
        if header != HEADER_MAGIC:
            print("Invalid file format (missing header).")
            sys.exit(1)
    
        key_len = file_data[len(HEADER_MAGIC)]
        if key_len not in (16, 32):
            print("Invalid key length in file header.")
            sys.exit(1)
    
        salt = file_data[len(HEADER_MAGIC)+1 : len(HEADER_MAGIC)+1+SALT_SIZE]
        iv_start = len(HEADER_MAGIC)+1+SALT_SIZE
        iv = file_data[iv_start : iv_start+IV_SIZE]
        ciphertext = file_data[iv_start+IV_SIZE:]
    
        password = get_password(confirm=False)
        key = PBKDF2(password, salt, dkLen=key_len, count=PBKDF2_ITERATIONS)
    
        # Decrypt and unpad the data
        cipher = AES.new(key, AES.MODE_CBC, iv)
        try:
            plaintext_padded = cipher.decrypt(ciphertext)
            plaintext = unpad(plaintext_padded, AES.block_size)
        except (ValueError, KeyError):
            print("Decryption failed. Incorrect password or corrupted file.")
            sys.exit(1)
    
        # Determine output file name
        if filepath.endswith(".enc"):
            out_filepath = filepath[:-4]  # remove the .enc extension
        else:
            out_filepath = filepath + ".dec"
    
        try:
            with open(out_filepath, "wb") as f:
                f.write(plaintext)
        except IOError as e:
            print(f"Error writing decrypted file: {e}")
            sys.exit(1)
    
        print(f"Decryption successful. Decrypted file: {out_filepath}")
    
    def main():
        print("AES Encryption/Decryption Utility")
        mode = input("Do you want to (E)ncrypt or (D)ecrypt? ").strip().lower()
        if mode not in ("e", "d"):
            print("Invalid mode selection.")
            sys.exit(1)
    
        files = list_files()
        selected_file = choose_file(files)
    
        if mode == "e":
            encrypt_file(selected_file)
        else:
            decrypt_file(selected_file)
    
    if __name__ == "__main__":
        main()
    
  • Interactive Image Converter Swift Script

    This Swift script guides users through selecting an input image from common directories using numbered lists and then converting it to a desired format (PNG, JPEG, or TIFF) while also letting them choose an output directory and filename. It simplifies the image conversion process on macOS by providing an interactive, user-friendly interface.

    Click to view script…
    #!/usr/bin/env swift
    import Foundation
    import AppKit
    
    // MARK: - Helper Functions
    
    /// Prompts the user for input with a message.
    func prompt(_ message: String) -> String {
        print(message, terminator: " ")
        return readLine() ?? ""
    }
    
    /// Presents a numbered list of directory options to the user and returns the selected directory URL.
    func selectDirectory(options: [(name: String, url: URL)], promptMessage: String) -> URL {
        for (index, option) in options.enumerated() {
            print("\(index + 1): \(option.name) -> \(option.url.path)")
        }
        let choiceStr = prompt(promptMessage)
        if let choiceNum = Int(choiceStr), choiceNum > 0, choiceNum <= options.count {
            let selected = options[choiceNum - 1]
            if selected.name.hasPrefix("Other") {
                let customPath = prompt("Enter custom directory path:")
                return URL(fileURLWithPath: customPath, isDirectory: true)
            } else {
                return selected.url
            }
        } else {
            let manualPath = prompt("Invalid selection. Enter directory path manually:")
            return URL(fileURLWithPath: manualPath, isDirectory: true)
        }
    }
    
    /// Presents a numbered list of files for the user to choose from.
    func selectFile(from files: [URL], in directory: URL) -> URL {
        for (index, file) in files.enumerated() {
            print("\(index + 1): \(file.lastPathComponent)")
        }
        let choiceStr = prompt("Enter the number of your choice:")
        if let choiceNum = Int(choiceStr), choiceNum > 0, choiceNum <= files.count {
            return files[choiceNum - 1]
        } else {
            let manualFile = prompt("Invalid selection. Enter the file name (in \(directory.path)):")
            return directory.appendingPathComponent(manualFile)
        }
    }
    
    /// Converts a file size (in bytes) to a human-readable string.
    func humanReadableSize(_ size: UInt64) -> String {
        let formatter = ByteCountFormatter()
        formatter.countStyle = .file
        formatter.allowedUnits = [.useKB, .useMB, .useGB, .useTB]
        return formatter.string(fromByteCount: Int64(size))
    }
    
    // MARK: - Candidate Directories Setup
    
    let fileManager = FileManager.default
    var candidateDirs: [(name: String, url: URL)] = []
    
    if let desktop = fileManager.urls(for: .desktopDirectory, in: .userDomainMask).first {
        candidateDirs.append(("Desktop", desktop))
    }
    if let documents = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
        candidateDirs.append(("Documents", documents))
    }
    if let downloads = fileManager.urls(for: .downloadsDirectory, in: .userDomainMask).first {
        candidateDirs.append(("Downloads", downloads))
    }
    candidateDirs.append(("Other (enter path manually)", URL(fileURLWithPath: fileManager.currentDirectoryPath)))
    
    // MARK: - Input Directory and File Selection
    
    print("Select the input directory:")
    let inputDirectory = selectDirectory(options: candidateDirs, promptMessage: "Enter the number of your choice:")
    
    // Verify the input directory exists.
    var isDir: ObjCBool = false
    guard fileManager.fileExists(atPath: inputDirectory.path, isDirectory: &isDir), isDir.boolValue else {
        print("Directory \(inputDirectory.path) does not exist or is not a directory.")
        exit(1)
    }
    
    // Define supported image extensions.
    let validImageExtensions: Set<String> = ["png", "jpg", "jpeg", "tiff", "gif"]
    
    // Get and filter files in the chosen directory.
    guard let filesInDirectory = try? fileManager.contentsOfDirectory(at: inputDirectory,
                                                                       includingPropertiesForKeys: nil,
                                                                       options: [.skipsHiddenFiles]) else {
        print("Unable to read contents of directory \(inputDirectory.path)")
        exit(1)
    }
    let imageFiles = filesInDirectory.filter { validImageExtensions.contains($0.pathExtension.lowercased()) }
    
    if imageFiles.isEmpty {
        print("No image files found in \(inputDirectory.path)")
        exit(1)
    }
    
    print("\nSelect the image file to convert:")
    let inputFileURL = selectFile(from: imageFiles, in: inputDirectory)
    
    // Optionally, display file size.
    if let attrs = try? fileManager.attributesOfItem(atPath: inputFileURL.path),
       let fileSize = attrs[.size] as? UInt64 {
        print("Selected file: \(inputFileURL.lastPathComponent) (\(humanReadableSize(fileSize)))")
    }
    
    // MARK: - Output Format Selection
    
    print("\nSelect the desired output image format:")
    print("1: PNG")
    print("2: JPEG")
    print("3: TIFF")
    let formatChoiceStr = prompt("Enter the number of your choice:")
    var outputFileType: NSBitmapImageRep.FileType
    var outputExtension: String
    
    switch formatChoiceStr {
    case "1":
        outputFileType = .png
        outputExtension = "png"
    case "2":
        outputFileType = .jpeg
        outputExtension = "jpg"
    case "3":
        outputFileType = .tiff
        outputExtension = "tiff"
    default:
        print("Invalid selection.")
        exit(1)
    }
    
    // MARK: - Output Directory and File Name Selection
    
    print("\nSelect the output directory:")
    let outputDirectory = selectDirectory(options: candidateDirs, promptMessage: "Enter the number of your choice:")
    
    isDir = false
    guard fileManager.fileExists(atPath: outputDirectory.path, isDirectory: &isDir), isDir.boolValue else {
        print("Output directory \(outputDirectory.path) does not exist or is not a directory.")
        exit(1)
    }
    
    let defaultOutputName = inputFileURL.deletingPathExtension().lastPathComponent + "_converted." + outputExtension
    let outputFileName = prompt("Enter output file name (default: \(defaultOutputName)):")
    let finalOutputFileName = outputFileName.isEmpty ? defaultOutputName : outputFileName
    let outputFileURL = outputDirectory.appendingPathComponent(finalOutputFileName)
    
    // MARK: - Image Conversion
    
    print("\nConverting image...")
    
    guard let inputImage = NSImage(contentsOf: inputFileURL) else {
        print("Failed to load image from \(inputFileURL.path)")
        exit(1)
    }
    guard let tiffData = inputImage.tiffRepresentation else {
        print("Failed to get TIFF representation of the image.")
        exit(1)
    }
    guard let bitmapRep = NSBitmapImageRep(data: tiffData) else {
        print("Failed to create bitmap representation.")
        exit(1)
    }
    guard let outputImageData = bitmapRep.representation(using: outputFileType, properties: [:]) else {
        print("Failed to convert image to selected format.")
        exit(1)
    }
    
    do {
        try outputImageData.write(to: outputFileURL)
        print("Image successfully converted and saved to \(outputFileURL.path)")
    } catch {
        print("Error saving converted image: \(error)")
        exit(1)
    }
  • macOS File Finder Swift Script

    This Swift script allows users to search for files of a specific type in a chosen directory on macOS. It interactively prompts for a directory (from common options or a custom path) and a file extension, then recursively locates matching files and displays their names along with human-readable file sizes.

    Click to view script…
    #!/usr/bin/env swift
    
    import Foundation
    
    // MARK: - Helper: Format File Size in Human-Readable Format
    func humanReadableSize(_ size: UInt64) -> String {
        let formatter = ByteCountFormatter()
        formatter.countStyle = .file
        // Allow units from bytes up to TB
        formatter.allowedUnits = [.useKB, .useMB, .useGB, .useTB]
        return formatter.string(fromByteCount: Int64(size))
    }
    
    // MARK: - Helper: Prompt for User Input
    func prompt(_ message: String) -> String {
        print(message, terminator: " ")
        return readLine() ?? ""
    }
    
    // MARK: - Prepare Candidate Directories
    let fileManager = FileManager.default
    var candidateDirectories: [String: URL] = [:]
    
    if let desktopURL = fileManager.urls(for: .desktopDirectory, in: .userDomainMask).first {
        candidateDirectories["Desktop"] = desktopURL
    }
    if let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
        candidateDirectories["Documents"] = documentsURL
    }
    if let downloadsURL = fileManager.urls(for: .downloadsDirectory, in: .userDomainMask).first {
        candidateDirectories["Downloads"] = downloadsURL
    }
    // Provide an option for a custom directory.
    candidateDirectories["Other"] = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
    
    // MARK: - Display Directory Options
    print("Select a directory to search in:")
    let directoryKeys = Array(candidateDirectories.keys).sorted() // sorted for consistent ordering
    for (index, key) in directoryKeys.enumerated() {
        if key == "Other" {
            print("\(index + 1): Enter a custom directory path")
        } else if let url = candidateDirectories[key] {
            print("\(index + 1): \(key) -> \(url.path)")
        }
    }
    
    guard let dirChoiceStr = readLine(), let choiceNum = Int(dirChoiceStr),
          choiceNum > 0, choiceNum <= directoryKeys.count else {
        print("Invalid directory choice. Exiting.")
        exit(1)
    }
    
    let chosenKey = directoryKeys[choiceNum - 1]
    var searchDirectory: URL
    
    if chosenKey == "Other" {
        let customPath = prompt("Enter full directory path:")
        searchDirectory = URL(fileURLWithPath: customPath, isDirectory: true)
    } else {
        searchDirectory = candidateDirectories[chosenKey]!
    }
    
    // Verify the directory exists
    var isDir: ObjCBool = false
    guard fileManager.fileExists(atPath: searchDirectory.path, isDirectory: &isDir), isDir.boolValue else {
        print("The selected path does not exist or is not a directory. Exiting.")
        exit(1)
    }
    
    // MARK: - Get File Extension to Search For
    let fileExtensionInput = prompt("Enter file extension to search for (e.g., txt, pdf, jpg):")
    let fileExtension = fileExtensionInput.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
    
    if fileExtension.isEmpty {
        print("No file extension provided. Exiting.")
        exit(1)
    }
    
    // Validate file extension (alphanumeric only)
    let regex = try! NSRegularExpression(pattern: "^[a-zA-Z0-9]+$")
    let range = NSRange(location: 0, length: fileExtension.utf16.count)
    if regex.firstMatch(in: fileExtension, options: [], range: range) == nil {
        print("Invalid file extension provided. Exiting.")
        exit(1)
    }
    
    // MARK: - Search for Files
    print("\nSearching for .\(fileExtension) files in \(searchDirectory.path)...")
    
    guard let enumerator = fileManager.enumerator(at: searchDirectory,
                                                    includingPropertiesForKeys: [.fileSizeKey],
                                                    options: [.skipsHiddenFiles],
                                                    errorHandler: { (url, error) -> Bool in
                                                        print("Error accessing \(url.path): \(error.localizedDescription)")
                                                        return true
                                                    }) else {
        print("Failed to create directory enumerator. Exiting.")
        exit(1)
    }
    
    var foundFiles: [(url: URL, size: UInt64)] = []
    
    while let fileURL = enumerator.nextObject() as? URL {
        if fileURL.pathExtension.lowercased() == fileExtension {
            do {
                let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey])
                if let fileSize = resourceValues.fileSize {
                    foundFiles.append((fileURL, UInt64(fileSize)))
                }
            } catch {
                print("Error getting size for \(fileURL.path): \(error.localizedDescription)")
            }
        }
    }
    
    // MARK: - Display Results
    if foundFiles.isEmpty {
        print("No files with .\(fileExtension) extension were found in \(searchDirectory.path).")
    } else {
        print("\nFound \(foundFiles.count) file(s):")
        for (file, size) in foundFiles {
            print("\(file.lastPathComponent) - \(humanReadableSize(size))")
        }
    }
  • macOS App Version Finder Bash Script

    This interactive script scans your /Applications and ~/Applications directories to list installed apps, allowing you to select one and view its version. It supports multiple searches and includes a built-in quit option for easy use.

    Click to view script…
    #!/bin/bash
    # Interactive App Version Finder for macOS with multi-search capability
    
    # Define directories to search for applications.
    APP_DIRS=("/Applications" "$HOME/Applications")
    
    # Initialize an array to store found apps.
    apps=()
    
    # Search for .app directories (non-recursive) in defined directories.
    for dir in "${APP_DIRS[@]}"; do
      if [ -d "$dir" ]; then
        while IFS= read -r -d $'\0' app; do
          apps+=("$app")
        done < <(find "$dir" -maxdepth 1 -type d -name "*.app" -print0)
      fi
    done
    
    # Check if any apps were found.
    if [ ${#apps[@]} -eq 0 ]; then
      echo "No applications found in ${APP_DIRS[*]}."
      exit 1
    fi
    
    # Main interactive loop.
    while true; do
      echo ""
      echo "Available Applications:"
      for i in "${!apps[@]}"; do
        echo "[$i] $(basename "${apps[$i]}")"
      done
      echo "[q] Quit"
      
      read -p "Enter the number of the app to check its version (or 'q' to quit): " input
    
      # Check for the quit option.
      if [[ "$input" =~ ^[Qq]$ ]]; then
        echo "Exiting."
        exit 0
      fi
    
      # Validate input is a number and within range.
      if ! [[ "$input" =~ ^[0-9]+$ ]] || [ "$input" -ge "${#apps[@]}" ]; then
        echo "Invalid selection. Please try again."
        continue
      fi
    
      APP_PATH="${apps[$input]}"
      APP_NAME=$(basename "$APP_PATH" .app)
    
      # Retrieve the version information from the app's Info.plist.
      VERSION=$(defaults read "$APP_PATH/Contents/Info" CFBundleShortVersionString 2>/dev/null)
    
      if [ -z "$VERSION" ]; then
        echo "Version information not found for $APP_NAME."
      else
        echo "$APP_NAME version: $VERSION"
      fi
    
      echo ""
      # Ask if the user wants to perform another search.
      read -p "Do you want to search for another app? (y/n): " answer
      if [[ ! "$answer" =~ ^[Yy]$ ]]; then
        echo "Exiting."
        exit 0
      fi
    done
  • QR Code Generator Python Script

    This Python script uses the qrcode library to convert user-provided text or URLs into a QR code image, which is saved as a PNG file. It offers a simple way to quickly generate and share QR codes directly from your terminal.

    Click to view script…
    #!/usr/bin/env python3
    import qrcode
    
    def generate_qr_code(data, filename="qr_code.png"):
        """
        Generates a QR code from the provided data and saves it to a file.
    
        Parameters:
            data (str): The text or URL to encode.
            filename (str): The name of the file to save the QR code image.
        
        Returns:
            str: The filename where the QR code is saved.
        """
        # Create a QRCode object with desired settings.
        qr = qrcode.QRCode(
            version=1,  # 1 means 21x21 matrix; increase for more data.
            error_correction=qrcode.constants.ERROR_CORRECT_H,  # High error correction.
            box_size=10,  # Size of each box in pixels.
            border=4,  # Border size in boxes.
        )
        
        # Add data to the QR code.
        qr.add_data(data)
        qr.make(fit=True)
        
        # Create an image from the QR code instance.
        img = qr.make_image(fill_color="black", back_color="white")
        img.save(filename)
        return filename
    
    def main():
        print("=== QR Code Generator ===\n")
        data = input("Enter the text or URL you want to encode in the QR code: ").strip()
        if not data:
            print("No data provided. Exiting.")
            return
    
        filename = generate_qr_code(data)
        print(f"\nQR code generated and saved as '{filename}'.")
    
    if __name__ == "__main__":
        main()
  • Historical News Fun Facts Python Script

    This Python script provides an interactive and engaging way to learn about major historical news events. Users can choose from a variety of categories and receive a random, interesting fact from each, making history both fun and informative.

    Click to view script…
    #!/usr/bin/env python3
    import random
    
    def main():
        categories = {
            "1": {
                "name": "Wars & Conflicts",
                "facts": [
                    "World War II (1939-1945) saw the largest mobilization of military forces in history.",
                    "The American Civil War (1861-1865) led to the abolition of slavery in the United States.",
                    "World War I (1914-1918) introduced modern warfare techniques like tanks and chemical warfare.",
                    "The Vietnam War sparked significant anti-war protests and shaped global politics for decades.",
                    "The Korean War (1950-1953) ended in an armistice, leaving the Korean Peninsula divided.",
                    "The Gulf War (1990-1991) was one of the first conflicts broadcast live on television.",
                    "The Napoleonic Wars reshaped Europe in the early 19th century.",
                    "The Crimean War (1853-1856) saw the first use of the telegraph for military communication.",
                    "The Thirty Years' War (1618-1648) was one of the longest and most destructive conflicts in Europe.",
                    "The Falklands War (1982) highlighted the lingering effects of colonial disputes."
                ]
            },
            "2": {
                "name": "Political Milestones",
                "facts": [
                    "The fall of the Berlin Wall in 1989 symbolized the end of the Cold War.",
                    "Nelson Mandela's release in 1990 marked a significant step toward ending apartheid in South Africa.",
                    "The Declaration of Independence in 1776 laid the foundation for the United States.",
                    "The French Revolution (1789-1799) radically transformed France’s political and social structure.",
                    "India gained independence from British colonial rule in 1947.",
                    "The establishment of the United Nations in 1945 aimed to promote peace and cooperation globally.",
                    "The suffragette movement secured voting rights for women in many countries during the early 20th century.",
                    "The end of the Cold War in 1991 reshaped global political alliances.",
                    "The formation of the European Union helped unify post-war Europe.",
                    "The Arab Spring in the early 2010s spurred political change across several Middle Eastern countries."
                ]
            },
            "3": {
                "name": "Tech & Science Breakthroughs",
                "facts": [
                    "The Apollo 11 moon landing in 1969 was watched live by millions around the globe.",
                    "The invention of the internet revolutionized communication and information sharing.",
                    "The Human Genome Project, completed in 2003, mapped the entire human genetic code.",
                    "The launch of the Hubble Space Telescope in 1990 expanded our view of the universe.",
                    "The discovery of penicillin in 1928 marked the beginning of modern antibiotics.",
                    "The advent of smartphones has transformed everyday communication and connectivity.",
                    "ENIAC, one of the first computers built in 1945, paved the way for the computer age.",
                    "Quantum computing promises to tackle problems beyond the capabilities of classical computers.",
                    "The discovery of the Higgs boson in 2012 confirmed the mechanism that gives particles mass.",
                    "CRISPR technology is revolutionizing genetics through precise gene editing."
                ]
            },
            "4": {
                "name": "Natural Disasters",
                "facts": [
                    "The 2004 Indian Ocean tsunami is one of the deadliest natural disasters in modern history.",
                    "The eruption of Mount Vesuvius in AD 79 famously buried the Roman cities of Pompeii and Herculaneum.",
                    "The 1906 San Francisco earthquake led to massive destruction and a major rebuild of the city.",
                    "Hurricane Katrina in 2005 devastated the Gulf Coast region of the United States.",
                    "The Great Galveston Hurricane of 1900 remains the deadliest natural disaster in U.S. history.",
                    "The 2011 Tōhoku earthquake and tsunami in Japan triggered a nuclear crisis at Fukushima.",
                    "The Dust Bowl of the 1930s severely impacted agriculture and displaced thousands in the American Midwest.",
                    "The 1980 eruption of Mount St. Helens was one of the most significant volcanic events in U.S. history.",
                    "The Lisbon earthquake of 1755 had far-reaching effects on European philosophy and society.",
                    "The Bhola cyclone of 1970 was one of the most catastrophic tropical cyclones ever recorded."
                ]
            },
            "5": {
                "name": "Cultural & Entertainment Events",
                "facts": [
                    "The Beatles’ appearance on The Ed Sullivan Show in 1964 kick-started Beatlemania in the U.S.",
                    "Woodstock in 1969 became an iconic symbol of the counterculture movement.",
                    "The launch of MTV in 1981 revolutionized the music industry and pop culture.",
                    "Blockbuster movies emerged in the 1970s, forever changing the film industry.",
                    "The first Academy Awards in 1929 established a global benchmark for cinematic achievement.",
                    "Reality TV's rise in the early 2000s reshaped modern television programming.",
                    "The global popularity of video games has transformed entertainment and technology industries.",
                    "Social media platforms in the 21st century have redefined celebrity culture and public interaction.",
                    "Streaming services have dramatically changed how audiences consume films and television shows.",
                    "Internet memes have become a defining aspect of digital culture in recent years."
                ]
            }
        }
        
        while True:
            print("\n=== Fun Fact Utility: Biggest News Events in History ===")
            print("Choose a category for a fun fact:")
            for key, value in categories.items():
                print(f"{key}. {value['name']}")
            print("Q. Quit")
            
            choice = input("Enter your choice: ").strip().lower()
            if choice == 'q':
                print("Goodbye!")
                break
            elif choice in categories:
                fact = random.choice(categories[choice]["facts"])
                print(f"\nFun Fact from {categories[choice]['name']}:")
                print(fact)
            else:
                print("Invalid choice. Please select a valid category.")
    
    if __name__ == "__main__":
        main()
  • URL Shortener Python Script

    This script is a straightforward and user-friendly URL shortener that leverages the TinyURL API to convert long URLs into compact, shareable links. It prompts the user for a URL, processes it through the API, and then displays both the original and shortened URLs for easy reference.

    This script requires the requests library to handle HTTP requests to the TinyURL API. Make sure to install it with pip install requests before running the script.

    Click to view script…
    #!/usr/bin/env python3
    import requests
    
    def shorten_url(url):
        """
        Shortens the given URL using the TinyURL API.
        
        Parameters:
            url (str): The original URL.
        
        Returns:
            str: The shortened URL or None if something went wrong.
        """
        api_url = 'https://tinyurl.com/api-create.php'
        params = {'url': url}
        try:
            response = requests.get(api_url, params=params)
            if response.status_code == 200:
                return response.text.strip()
            else:
                print(f"Error: Unable to shorten URL. Status code: {response.status_code}")
                return None
        except Exception as e:
            print("Error during request:", e)
            return None
    
    def main():
        print("=== Welcome to the Python URL Shortener ===\n")
        
        # Get URL from user
        original_url = input("Enter the URL you want to shorten: ").strip()
        if not original_url:
            print("No URL provided. Exiting.")
            return
    
        # Get the shortened URL
        shortened_url = shorten_url(original_url)
        if shortened_url:
            # Display the original and shortened URLs
            print("\nHere are your URLs:")
            print("-" * 40)
            print("Original URL:  ", original_url)
            print("Shortened URL: ", shortened_url)
            print("-" * 40)
        else:
            print("Failed to shorten the URL.")
    
    if __name__ == "__main__":
        main()

  • Interactive macOS Countdown Timer Bash Script

    This Bash script creates a countdown timer that either accepts the duration in seconds as a command-line argument or prompts the user for input. It displays the remaining time in the Terminal and sends a native macOS notification when the countdown completes.

    Click to view script…
    #!/bin/bash
    # Simple Countdown Timer for macOS with Interactive Input
    
    # Check if a time (in seconds) was provided as an argument
    if [ $# -eq 0 ]; then
        # Prompt the user for input if no argument is given
        read -p "Enter number of seconds for the countdown: " TIME_LEFT
    else
        TIME_LEFT=$1
    fi
    
    # Validate that the input is a positive integer
    if ! [[ $TIME_LEFT =~ ^[0-9]+$ ]] ; then
        echo "Error: Please enter a positive integer for seconds." >&2
        exit 1
    fi
    
    # Countdown loop
    while [ $TIME_LEFT -gt 0 ]; do
        echo -ne "Time left: ${TIME_LEFT} second(s) \r"
        sleep 1
        ((TIME_LEFT--))
    done
    
    # Move to a new line after the countdown
    echo -e "\nTime's up!"
    
    # Display a macOS notification using osascript
    osascript -e 'display notification "Countdown Complete!" with title "Timer"'
  • Automated Screen Capture AppleScript Tool

    This AppleScript lets you capture screenshots either on demand or at scheduled intervals, automatically saving each capture in a designated folder with a unique, timestamped filename. Notifications alert you whenever a screenshot is taken, ensuring you always know when your screen has been captured.

    Warning: This script requires specific macOS permissions to function correctly. In particular, macOS Catalina and later require that you enable Screen Recording permissions for the app running the script (e.g., Script Editor or Terminal), and you may also need to grant Automation or Full Disk Access in Security & Privacy settings. Without these permissions, the screenshot functionality and notifications might not work as expected.

    Click to view script…
    -- Automated Screen Capture Script
    
    -- Ensure the "Screenshots" folder exists on the Desktop
    do shell script "mkdir -p ~/Desktop/Screenshots"
    
    -- Ask the user to choose a capture mode
    display dialog "Choose Screenshot Mode:" buttons {"On Demand", "Scheduled"} default button "On Demand"
    set modeChoice to button returned of result
    
    if modeChoice is "On Demand" then
        my takeScreenshot()
    else if modeChoice is "Scheduled" then
        display dialog "Enter interval between screenshots (in seconds):" default answer "60"
        set intervalSeconds to (text returned of result) as integer
        
        display dialog "Enter number of screenshots to capture:" default answer "10"
        set numberShots to (text returned of result) as integer
        
        repeat with i from 1 to numberShots
            my takeScreenshot()
            delay intervalSeconds
        end repeat
    end if
    
    -- Handler to take a screenshot and save it with a timestamped filename
    on takeScreenshot()
        -- Create a timestamp
        set timeStamp to do shell script "date +%Y%m%d-%H%M%S"
        set fileName to "Screenshot-" & timeStamp & ".png"
        set filePath to "~/Desktop/Screenshots/" & fileName
        -- Capture the screenshot silently (-x suppresses the shutter sound)
        do shell script "screencapture -x " & filePath
        display notification "Captured: " & fileName with title "Screen Capture"
    end takeScreenshot
  • Flexible Volume Controller AppleScript

    This AppleScript lets you adjust your Mac’s system volume either by selecting from a list of predefined levels (Mute, 0, 25, 50, 75, or 100) or by entering a custom value between 0 and 100, with a notification confirming the change.

    Click to view script…
    -- Volume Controller Script
    
    -- First, choose the control method.
    display dialog "Choose Volume Control Method:" buttons {"Predefined Levels", "Custom Level"} default button "Predefined Levels"
    set methodChoice to button returned of result
    
    if methodChoice is "Predefined Levels" then
        -- Use a list to display multiple predefined options.
        set volumeOptions to {"Mute", "0", "25", "50", "75", "100"}
        set volChoiceList to choose from list volumeOptions with prompt "Select Volume Setting:" default items {"50"}
        
        if volChoiceList is false then
            display notification "No selection made." with title "Volume Controller"
            return
        end if
        
        set volChoice to item 1 of volChoiceList
        if volChoice is "Mute" then
            set volume with output muted
            display notification "Volume muted" with title "Volume Controller"
        else
            set volValue to volChoice as integer
            set volume output volume volValue
            display notification "Volume set to " & volValue & "%" with title "Volume Controller"
        end if
        
    else if methodChoice is "Custom Level" then
        display dialog "Enter desired volume level (0-100):" default answer "50"
        set volInput to text returned of result
        set volLevel to volInput as integer
        
        if volLevel < 0 then
            set volLevel to 0
        else if volLevel > 100 then
            set volLevel to 100
        end if
        
        set volume output volume volLevel
        display notification "Volume set to " & volLevel & "%" with title "Volume Controller"
    end if