initial commit

main
_ 2025-01-09 02:49:39 +00:00
commit f911fb133f
4 changed files with 197 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/untracked

103
Cargo.lock generated Normal file
View File

@ -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"

9
Cargo.toml Normal file
View File

@ -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"

83
src/main.rs Normal file
View File

@ -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<String, Event>,
}
#[derive(Deserialize)]
struct Event {
id: String,
message: Option<Message>,
}
#[derive(Deserialize)]
struct Message {
author: Author,
create_time: Option<f64>,
content: Content,
}
#[derive(Deserialize)]
struct Author {
role: String,
}
#[derive(Deserialize)]
struct Content {
content_type: String,
parts: Option<Vec<String>>,
}
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<Conversation> = serde_json::from_reader(rdr)?;
for conv in &conversations {
println!("{} - {}", conv.id, conv.title);
}
Ok(())
}