From f911fb133f8bc262a3109de99de5afbd2120da54 Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Thu, 9 Jan 2025 02:49:39 +0000 Subject: [PATCH] initial commit --- .gitignore | 2 + Cargo.lock | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 9 +++++ src/main.rs | 83 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..350f73e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/untracked diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4c924f8 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,103 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "chatgpt-export" +version = "0.1.0" +dependencies = [ + "anyhow", + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f33048d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "chatgpt-export" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.95" +serde = {version = "1.0.217", features = ["derive"]} +serde_json = "1.0.135" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c8ec718 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,83 @@ +//! Exports conversations from ChatGPT dumps as Markdown +//! +//! Example: +//! +//! ```bash +//! cargo run -- list < conversations.json > to-export.txt +//! nano to-export.txt # Remove any conversations you don't want exported +//! cargo run -- export to-export.txt < conversations.json +//! ``` + +use std::collections::BTreeMap; + +use anyhow::{bail, Result}; +use serde::Deserialize; + +#[derive(Deserialize)] +struct Conversation { + id: String, + title: String, + mapping: BTreeMap, +} + +#[derive(Deserialize)] +struct Event { + id: String, + message: Option, +} + +#[derive(Deserialize)] +struct Message { + author: Author, + create_time: Option, + content: Content, +} + +#[derive(Deserialize)] +struct Author { + role: String, +} + +#[derive(Deserialize)] +struct Content { + content_type: String, + parts: Option>, +} + +enum Command { + Export, + List, +} + +fn main() -> Result<()> { + let mut args = std::env::args(); + // Skip the exe name + args.next().unwrap(); + + let mut cmd = None; + while let Some(arg) = args.next() { + match arg.as_str() { + "export" => cmd = Some(Command::Export), + "list" => cmd = Some(Command::List), + _ => bail!("Can't understand command line"), + } + } + + match cmd { + Some(Command::Export) => unimplemented!(), + Some(Command::List) => list_conversations(), + None => bail!("Subcommand is required, e.g. `list` or `export`"), + } +} + +fn list_conversations() -> Result<()> { + eprintln!("Awaiting conversation.json on stdin..."); + let rdr = std::io::stdin().lock(); + let conversations: Vec = serde_json::from_reader(rdr)?; + + for conv in &conversations { + println!("{} - {}", conv.id, conv.title); + } + + Ok(()) +}