commit 7c49de7e55b133639d74920a766c46ab9fc2d271 Author: Ada Werefox Date: Wed Jun 10 13:44:05 2026 -0700 I hate how good AI is at doing menial straightforward tasks. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb534d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.zuliprc +.mypy_cache/** +.venv/** +__pycache__/** +build/** +dist/** +*.md \ No newline at end of file diff --git a/.zuliprc.template b/.zuliprc.template new file mode 100644 index 0000000..636b4f8 --- /dev/null +++ b/.zuliprc.template @@ -0,0 +1,4 @@ +[api] +email=me@example.com +key=some_api_key +site=https://zulip.example.com diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dcbb5ed --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +certifi==2026.5.20 +charset-normalizer==3.4.7 +click==8.4.1 +colorama==0.4.6 +distro==1.9.0 +idna==3.18 +requests==2.34.2 +typing_extensions==4.15.0 +urllib3==2.7.0 +zulip==0.9.1 diff --git a/topic-export.py b/topic-export.py new file mode 100644 index 0000000..c33540f --- /dev/null +++ b/topic-export.py @@ -0,0 +1,102 @@ +import datetime +import zulip +import sys +import os + +# Check if running as a PyInstaller bundle or raw python script +if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): + # Path inside the temporary binary folder + base_path = sys._MEIPASS # type: ignore +else: + # Path in your standard local workspace folder + base_path = os.path.abspath(".") + +CONFIG_PATH = os.path.join(base_path, ".zuliprc") +CHANNEL_NAME = "wifey-time" +TOPIC_NAME = "Mutual Enjoyment" +OUTPUT_FILE = ( + f"{datetime.date.today().isoformat()}_Mutual_Enjoyment.md" # Saved directly as .md +) + +# Initialize client using the embedded configuration +client = zulip.Client(config_file=CONFIG_PATH) + +narrow_filter = [ + {"operator": "channel", "operand": CHANNEL_NAME}, + {"operator": "topic", "operand": TOPIC_NAME}, +] + +anchor = "oldest" +all_messages = [] +batch_count = 0 + +print(f"Starting export for '{CHANNEL_NAME}' > '{TOPIC_NAME}'...") + +# Step 1: Automatic Pagination Loop +while True: + batch_count += 1 + + request_params = { + "anchor": anchor, + "num_before": 0, + "num_after": 1000, + "narrow": narrow_filter, + "apply_markdown": False, # This ensures Zulip gives us raw markdown strings + } + + response = client.call_endpoint( + url="messages", + method="GET", + request=request_params, + ) + + if response.get("result") != "success": + print(f"Error fetching data: {response.get('msg')}") + break + + messages = response.get("messages", []) + if not messages: + break + + if anchor == "oldest": + all_messages.extend(messages) + else: + all_messages.extend(messages[1:]) + if len(messages) <= 1: + break + + anchor = messages[-1]["id"] + print(f" Batch {batch_count}: Downloaded {len(messages)} messages...") + +# Step 2: Format Data into a Valid Markdown File +if all_messages: + with open(OUTPUT_FILE, "w", encoding="utf-8") as f: + # Markdown File Header + f.write(f"# Chat Export: {CHANNEL_NAME} > {TOPIC_NAME}\n") + f.write(f"**Total Messages:** {len(all_messages)} \n") + f.write(f"**Export Date:** {datetime.date.today().isoformat()} \n") + f.write("\n---\n\n") + + # Process each individual message payload + for msg in all_messages: + sender = msg.get("sender_full_name", "Unknown User") + content = msg.get("content", "") + + # Convert Unix timestamp to a friendly readable format + timestamp = msg.get("timestamp", 0) + formatted_time = datetime.datetime.fromtimestamp(timestamp).strftime( + "%Y-%m-%d %H:%M:%S" + ) + + # Write metadata as a markdown bold header line + f.write(f"**[{formatted_time}] {sender}:**\n\n") + + # Print the raw markdown content exactly as typed in Zulip + f.write(f"{content}\n\n") + + # Add a clean markdown line break rule between chat entries + f.write(" \n") + + print(f"\nSuccess! Exported {len(all_messages)} messages to: '{OUTPUT_FILE}'.") +else: + print("\nNo messages discovered matching this specific channel and topic criteria.") diff --git a/topic-export.spec b/topic-export.spec new file mode 100644 index 0000000..df25527 --- /dev/null +++ b/topic-export.spec @@ -0,0 +1,45 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + +a = Analysis( + ['topic-export.py'], + pathex=[], + binaries=[], + datas=[('.zuliprc', '.')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) + +pyz = PYZ( + a.pure, + a.zipped_data, + cipher=block_cipher +) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name="export_mutual_enjoyment", + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) \ No newline at end of file