#!/usr/bin/env python3 """ @coolhand.bsky.social Because Fuck Fascists Optionally exports sales and product details to CSV, and then deletes all products, including canceling subscriptions. Date: 2023-11-15 """ import requests import json import csv import sys # ---------------------------- # ASCII Art Banner & Dependencies Check # ---------------------------- def print_banner(): banner = ''' ██████╗ ██╗ ██╗███╗ ███╗██████╗ ██╗██████╗ ██╔════╝ ██║ ██║████╗ ████║██╔══██╗██║██╔══██╗ ██║ ███╗██║ ██║██╔████╔██║██████╔╝██║██║ ██║ ██║ ██║██║ ██║██║╚██╔╝██║██╔══██╗██║██║ ██║ ╚██████╔╝╚██████╔╝██║ ╚═╝ ██║██║ ██║██║██████╔╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═════╝ ================================================ .:: BECAUSE FUCK FASCISTS ::. ================================================ - Delete ALL products from Gumroad account - Export sales and product data to CSV - Burn it all down ================================================ @coolhand.bsky.social | 2023-11-15 ''' print(banner) def check_dependencies(): try: import requests except ImportError: print("\n[!] Required package 'requests' not found") print("[+] Install required packages with:") print(" pip install requests\n") sys.exit(1) # Call these functions at startup print_banner() check_dependencies() # ---------------------------- # Configuration and Credentials # ---------------------------- BASE_API_URL = "https://api.gumroad.com/v2" LOG_FILE = "gumroad_api_log.txt" def get_access_token(): print("\nGumroad API Access Token Required") print("--------------------------------") print("To use this script, you need to create a Gumroad API access token:") print("1. Go to: https://gumroad.com/settings/advanced#application-form") print("2. Create a new application if you don't have one") print("3. Generate an access token for your application") print("4. Copy and paste the token below\n") token = input("Enter your Gumroad API access token: ").strip() if not token: print("Access token is required. Exiting...") sys.exit(1) return token # ---------------------------- # Utility Functions and Error Handling # ---------------------------- def log_output(text): with open(LOG_FILE, "a") as log_file: log_file.write(text + "\n") print(text) def make_request(endpoint, method, data=None, access_token=None): url = f"{BASE_API_URL}/{endpoint}" headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json" } try: if method.upper() == "GET": response = requests.get(url, headers=headers, timeout=120) elif method.upper() == "POST": response = requests.post(url, headers=headers, json=data, timeout=120) elif method.upper() == "PUT": response = requests.put(url, headers=headers, json=data, timeout=120) elif method.upper() == "DELETE": response = requests.delete(url, headers=headers, timeout=120) else: log_output(f"Unsupported HTTP method: {method}") return None resp_json = response.json() # Check for success field; if not true, log error if not resp_json.get("success", False): log_output(f"Error Response: {json.dumps(resp_json)}") return None log_output(json.dumps(resp_json)) return resp_json except Exception as e: log_output(f"Exception during API call: {e}") return None # ---------------------------- # Main Functions # ---------------------------- def delete_all_products(access_token): confirm = input("WARNING: This will delete ALL products. Proceed? (yes/no): ").strip() if confirm.lower() != "yes": log_output("Deletion aborted.") return # Offer to export sales data before deletion export_sales_choice = input("Would you like to export full sales data to CSV before deletion? (yes/no): ").strip() if export_sales_choice.lower() == "yes": export_sales_csv(access_token) # Offer to export product data before deletion export_products_choice = input("Would you like to export full product data to CSV before deletion? (yes/no): ").strip() if export_products_choice.lower() == "yes": export_products_csv(access_token) log_output("Fetching all products...") resp = make_request("products", "GET", access_token=access_token) if resp and "products" in resp: for prod in resp["products"]: prod_id = prod.get("id") log_output(f"Deleting Product ID: {prod_id}...") del_resp = make_request(f"products/{prod_id}", "DELETE", access_token=access_token) if del_resp: log_output(json.dumps(del_resp, indent=2)) else: log_output("No products found to delete.") def export_sales_csv(access_token): log_output("Exporting full sales data to CSV...") filename = input("Enter CSV filename (default: gumroad_export_sales.csv): ").strip() if not filename: filename = "gumroad_export_sales.csv" resp = make_request("sales", "GET", access_token=access_token) if not resp: log_output("No response from API.") return sales = resp.get("sales", []) if len(sales) == 0: with open(filename, "w", newline="") as csvfile: csvfile.write("No sales data found.") log_output(f"No sales exported; {filename} created with no data.") return # Use keys from first sale, sorted for consistency header_keys = sorted(sales[0].keys()) with open(filename, "w", newline="") as csvfile: writer = csv.writer(csvfile) writer.writerow(header_keys) # Process pages page_key = None while True: endpoint = "sales" if not page_key else f"sales?page_key={page_key}" resp = make_request(endpoint, "GET", access_token=access_token) if resp and "sales" in resp: for sale in resp["sales"]: # Sort values according to header_keys row = [sale.get(key, "") for key in header_keys] writer.writerow(row) page_key = resp.get("next_page_key") if not page_key or page_key == "null": break else: break log_output(f"Sales exported to {filename}.") def export_products_csv(access_token): log_output("Exporting full product data to CSV...") filename = input("Enter CSV filename (default: gumroad_export_products.csv): ").strip() if not filename: filename = "gumroad_export_products.csv" resp = make_request("products", "GET", access_token=access_token) if not resp: log_output("No response from API.") return products = resp.get("products", []) if len(products) == 0: with open(filename, "w", newline="") as csvfile: csvfile.write("No product data found.") log_output(f"No products exported; {filename} created with no data.") return header_keys = sorted(products[0].keys()) with open(filename, "w", newline="") as csvfile: writer = csv.writer(csvfile) writer.writerow(header_keys) for prod in products: row = [prod.get(key, "") for key in header_keys] writer.writerow(row) log_output(f"Products exported to {filename}.") # ---------------------------- # Main Interactive Loop # ---------------------------- def main(): # Get access token at the start access_token = get_access_token() while True: print("\nGumroad API Interactive Shell") print("1) Delete All Products") print("2) Export Sales to CSV") print("3) Export Products to CSV") print("4) Exit") choice = input("Choose an option: ").strip() if choice == "1": delete_all_products(access_token) elif choice == "2": export_sales_csv(access_token) elif choice == "3": export_products_csv(access_token) elif choice == "4": log_output("Exiting...") sys.exit(0) else: log_output("Invalid option, please try again.") input("\nPress Enter to continue...") if __name__ == '__main__': # Banner and dependency check are called at import time main()