Add some untested version of publishing a feed
Also adjust names of different env vars, and also adjust setup instructions
This commit is contained in:
		
							parent
							
								
									5128bf9d4a
								
							
						
					
					
						commit
						e95c4923d6
					
				| 
						 | 
				
			
			@ -1,3 +1,6 @@
 | 
			
		|||
PUBLISHER_BLUESKY_HANDLE="..."
 | 
			
		||||
PUBLISHER_BLUESKY_PASSWORD="..."
 | 
			
		||||
PUBLISHER_DID="..."
 | 
			
		||||
CHAT_GPT_API_KEY="fake-chat-gpt-key"
 | 
			
		||||
DATABASE_URL="postgres://postgres:password@localhost/nederlandskie"
 | 
			
		||||
HOSTNAME="..."
 | 
			
		||||
FEED_GENERATOR_HOSTNAME="..."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,6 +75,54 @@ dependencies = [
 | 
			
		|||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstream"
 | 
			
		||||
version = "0.5.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anstyle",
 | 
			
		||||
 "anstyle-parse",
 | 
			
		||||
 "anstyle-query",
 | 
			
		||||
 "anstyle-wincon",
 | 
			
		||||
 "colorchoice",
 | 
			
		||||
 "utf8parse",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstyle"
 | 
			
		||||
version = "1.0.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstyle-parse"
 | 
			
		||||
version = "0.2.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "utf8parse",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstyle-query"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows-sys",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anstyle-wincon"
 | 
			
		||||
version = "2.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anstyle",
 | 
			
		||||
 "windows-sys",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "anyhow"
 | 
			
		||||
version = "1.0.75"
 | 
			
		||||
| 
						 | 
				
			
			@ -430,6 +478,52 @@ dependencies = [
 | 
			
		|||
 "unsigned-varint",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap"
 | 
			
		||||
version = "4.4.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "clap_builder",
 | 
			
		||||
 "clap_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap_builder"
 | 
			
		||||
version = "4.4.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anstream",
 | 
			
		||||
 "anstyle",
 | 
			
		||||
 "clap_lex",
 | 
			
		||||
 "strsim",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap_derive"
 | 
			
		||||
version = "4.4.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "heck",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn 2.0.35",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "clap_lex"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "colorchoice"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "compact_str"
 | 
			
		||||
version = "0.7.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -2248,6 +2342,7 @@ dependencies = [
 | 
			
		|||
 "chat-gpt-lib-rs",
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "ciborium",
 | 
			
		||||
 "clap",
 | 
			
		||||
 "dotenv",
 | 
			
		||||
 "env_logger",
 | 
			
		||||
 "futures",
 | 
			
		||||
| 
						 | 
				
			
			@ -3284,6 +3379,12 @@ dependencies = [
 | 
			
		|||
 "unicode-normalization",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "strsim"
 | 
			
		||||
version = "0.10.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "strum"
 | 
			
		||||
version = "0.24.1"
 | 
			
		||||
| 
						 | 
				
			
			@ -3658,6 +3759,12 @@ version = "0.7.6"
 | 
			
		|||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "utf8parse"
 | 
			
		||||
version = "0.2.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "vcpkg"
 | 
			
		||||
version = "0.2.15"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
name = "nederlandskie"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
default-run = "nederlandskie"
 | 
			
		||||
 | 
			
		||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +15,7 @@ axum = "0.6.20"
 | 
			
		|||
chat-gpt-lib-rs = "0.2.1"
 | 
			
		||||
chrono = "0.4.31"
 | 
			
		||||
ciborium = "0.2.1"
 | 
			
		||||
clap = { version = "4.4.4", features = ["derive"] }
 | 
			
		||||
dotenv = "0.15.0"
 | 
			
		||||
env_logger = "0.10.0"
 | 
			
		||||
futures = "0.3.28"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								README.md
								
								
								
								
							
							
						
						
									
										24
									
								
								README.md
								
								
								
								
							| 
						 | 
				
			
			@ -15,14 +15,32 @@ Heavily WIP. Doesn't work yet at all, but does read the stream of posts as they
 | 
			
		|||
- [ ] Publish the feed
 | 
			
		||||
- [ ] Handle deleting of posts
 | 
			
		||||
 | 
			
		||||
## Initial setup
 | 
			
		||||
## Configuration
 | 
			
		||||
 | 
			
		||||
Copy `.env.example` into `.env` and set up the environment variables within:
 | 
			
		||||
1. Copy `.env.example` into `.env` and set up the environment variables within:
 | 
			
		||||
 | 
			
		||||
   - `PUBLISHER_BLUESKY_HANDLE` to your Bluesky handle
 | 
			
		||||
   - `PUBLISHER_BLUESKY_PASSWORD` to Bluesky app password that you created in settings
 | 
			
		||||
   - `CHAT_GPT_API_KEY` for your ChatGPT key
 | 
			
		||||
   - `DATABASE_URL` for PostgreSQL credentials
 | 
			
		||||
- `HOSTNAME` to the hostname of where you intend to host the feed
 | 
			
		||||
   - `FEED_GENERATOR_HOSTNAME` to the hostname of where you intend to host the feed
 | 
			
		||||
 | 
			
		||||
2. Determine your own DID and put it in `PUBLISHER_DID` env variable in `.env`:
 | 
			
		||||
 | 
			
		||||
   ```
 | 
			
		||||
   cargo run --bin who_am_i
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
## Running
 | 
			
		||||
 | 
			
		||||
### Populate and serve the feed
 | 
			
		||||
 | 
			
		||||
`cargo run`
 | 
			
		||||
 | 
			
		||||
### Determine your own did for publishing
 | 
			
		||||
 | 
			
		||||
`cargo run --bin who_am_i`
 | 
			
		||||
 | 
			
		||||
### Publish the feed
 | 
			
		||||
 | 
			
		||||
`cargo run --bin publish_feed -- --help`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
extern crate nederlandskie;
 | 
			
		||||
 | 
			
		||||
use std::env;
 | 
			
		||||
 | 
			
		||||
use anyhow::{Context, Result};
 | 
			
		||||
use clap::Parser;
 | 
			
		||||
use dotenv::dotenv;
 | 
			
		||||
 | 
			
		||||
use nederlandskie::services::Bluesky;
 | 
			
		||||
 | 
			
		||||
#[derive(Parser, Debug)]
 | 
			
		||||
struct Args {
 | 
			
		||||
    /// Short name of the feed. Must match one of the defined algos.
 | 
			
		||||
    #[arg(long)]
 | 
			
		||||
    name: String,
 | 
			
		||||
 | 
			
		||||
    /// Name that will be displayed in Bluesky interface
 | 
			
		||||
    #[arg(long)]
 | 
			
		||||
    display_name: String,
 | 
			
		||||
 | 
			
		||||
    /// Description that will be displayed in Bluesky interface
 | 
			
		||||
    #[arg(long)]
 | 
			
		||||
    description: String,
 | 
			
		||||
 | 
			
		||||
    /// Filename of the avatar that will be displayed
 | 
			
		||||
    #[arg(long)]
 | 
			
		||||
    avatar_filename: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<()> {
 | 
			
		||||
    dotenv()?;
 | 
			
		||||
 | 
			
		||||
    let args = Args::parse();
 | 
			
		||||
 | 
			
		||||
    let handle = env::var("PUBLISHER_BLUESKY_HANDLE")
 | 
			
		||||
        .context("PUBLISHER_BLUESKY_HANDLE environment variable must be set")?;
 | 
			
		||||
 | 
			
		||||
    let password = env::var("PUBLISHER_BLUESKY_PASSWORD")
 | 
			
		||||
        .context("PUBLISHER_BLUESKY_PASSWORD environment variable must be set")?;
 | 
			
		||||
 | 
			
		||||
    let feed_generator_did = format!("did:web:{}", env::var("FEED_GENERATOR_HOSTNAME")?);
 | 
			
		||||
 | 
			
		||||
    let bluesky = Bluesky::new("https://bsky.social");
 | 
			
		||||
 | 
			
		||||
    let session = bluesky.login(&handle, &password).await?;
 | 
			
		||||
 | 
			
		||||
    let mut avatar = None;
 | 
			
		||||
    if let Some(path) = args.avatar_filename {
 | 
			
		||||
        let bytes = std::fs::read(path)?;
 | 
			
		||||
        avatar = Some(bluesky.upload_blob(bytes).await?);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bluesky
 | 
			
		||||
        .publish_feed(
 | 
			
		||||
            &session.did,
 | 
			
		||||
            &feed_generator_did,
 | 
			
		||||
            &args.name,
 | 
			
		||||
            &args.display_name,
 | 
			
		||||
            &args.description,
 | 
			
		||||
            avatar,
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
extern crate nederlandskie;
 | 
			
		||||
 | 
			
		||||
use std::env;
 | 
			
		||||
 | 
			
		||||
use anyhow::{Context, Result};
 | 
			
		||||
use dotenv::dotenv;
 | 
			
		||||
 | 
			
		||||
use nederlandskie::services::Bluesky;
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() -> Result<()> {
 | 
			
		||||
    dotenv()?;
 | 
			
		||||
 | 
			
		||||
    let bluesky = Bluesky::new("https://bsky.social");
 | 
			
		||||
 | 
			
		||||
    let handle = env::var("PUBLISHER_BLUESKY_HANDLE")
 | 
			
		||||
        .context("PUBLISHER_BLUESKY_HANDLE environment variable must be set")?;
 | 
			
		||||
 | 
			
		||||
    let password = env::var("PUBLISHER_BLUESKY_PASSWORD")
 | 
			
		||||
        .context("PUBLISHER_BLUESKY_PASSWORD environment variable must be set")?;
 | 
			
		||||
 | 
			
		||||
    let session = bluesky.login(&handle, &password).await?;
 | 
			
		||||
 | 
			
		||||
    println!("{}", session.did);
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,9 +5,9 @@ use std::env;
 | 
			
		|||
pub struct Config {
 | 
			
		||||
    pub chat_gpt_api_key: String,
 | 
			
		||||
    pub database_url: String,
 | 
			
		||||
    pub service_did: String,
 | 
			
		||||
    pub feed_generator_did: String,
 | 
			
		||||
    pub publisher_did: String,
 | 
			
		||||
    pub hostname: String,
 | 
			
		||||
    pub feed_generator_hostname: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Config {
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +17,9 @@ impl Config {
 | 
			
		|||
        Ok(Self {
 | 
			
		||||
            chat_gpt_api_key: env::var("CHAT_GPT_API_KEY")?,
 | 
			
		||||
            database_url: env::var("DATABASE_URL")?,
 | 
			
		||||
            hostname: env::var("HOSTNAME")?,
 | 
			
		||||
            service_did: format!("did:web:{}", env::var("HOSTNAME")?),
 | 
			
		||||
            publisher_did: "".to_owned(), // TODO
 | 
			
		||||
            feed_generator_hostname: env::var("FEED_GENERATOR_HOSTNAME")?,
 | 
			
		||||
            feed_generator_did: format!("did:web:{}", env::var("FEED_GENERATOR_HOSTNAME")?),
 | 
			
		||||
            publisher_did: env::var("PUBLISHER_DID")?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ pub async fn describe_feed_generator(
 | 
			
		|||
    State(state): State<FeedServerState>,
 | 
			
		||||
) -> Json<FeedGeneratorDescription> {
 | 
			
		||||
    Json(FeedGeneratorDescription {
 | 
			
		||||
        did: state.config.service_did.clone(),
 | 
			
		||||
        did: state.config.feed_generator_did.clone(),
 | 
			
		||||
        feeds: state
 | 
			
		||||
            .algos
 | 
			
		||||
            .iter_names()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,11 +22,11 @@ pub struct Service {
 | 
			
		|||
pub async fn did_json(State(state): State<FeedServerState>) -> Json<Did> {
 | 
			
		||||
    Json(Did {
 | 
			
		||||
        context: vec!["https://www.w3.org/ns/did/v1".to_owned()],
 | 
			
		||||
        id: state.config.service_did.clone(),
 | 
			
		||||
        id: state.config.feed_generator_did.clone(),
 | 
			
		||||
        service: vec![Service {
 | 
			
		||||
            id: "#bsky_fg".to_owned(),
 | 
			
		||||
            type_: "BskyFeedGenerator".to_owned(),
 | 
			
		||||
            service_endpoint: format!("https://{}", state.config.hostname),
 | 
			
		||||
            service_endpoint: format!("https://{}", state.config.feed_generator_hostname),
 | 
			
		||||
        }],
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,12 +38,12 @@ impl PostIndexer {
 | 
			
		|||
 | 
			
		||||
        let cursor = self
 | 
			
		||||
            .database
 | 
			
		||||
            .fetch_subscription_cursor(&self.config.service_did)
 | 
			
		||||
            .fetch_subscription_cursor(&self.config.feed_generator_did)
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        if cursor.is_none() {
 | 
			
		||||
            self.database
 | 
			
		||||
                .create_subscription_state(&self.config.service_did)
 | 
			
		||||
                .create_subscription_state(&self.config.feed_generator_did)
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,10 +91,10 @@ impl CommitProcessor for PostIndexer {
 | 
			
		|||
        if commit.seq % 20 == 0 {
 | 
			
		||||
            info!(
 | 
			
		||||
                "Updating cursor for {} to {}",
 | 
			
		||||
                self.config.service_did, commit.seq
 | 
			
		||||
                self.config.feed_generator_did, commit.seq
 | 
			
		||||
            );
 | 
			
		||||
            self.database
 | 
			
		||||
                .update_subscription_cursor(&self.config.service_did, commit.seq)
 | 
			
		||||
                .update_subscription_cursor(&self.config.feed_generator_did, commit.seq)
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,10 @@
 | 
			
		|||
use anyhow::{anyhow, Result};
 | 
			
		||||
use atrium_api::blob::BlobRef;
 | 
			
		||||
use atrium_api::client::AtpServiceClient;
 | 
			
		||||
use atrium_api::client::AtpServiceWrapper;
 | 
			
		||||
use atrium_api::records::Record;
 | 
			
		||||
use atrium_xrpc::client::reqwest::ReqwestClient;
 | 
			
		||||
use chrono::Utc;
 | 
			
		||||
use futures::StreamExt;
 | 
			
		||||
use log::error;
 | 
			
		||||
use tokio_tungstenite::{connect_async, tungstenite};
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +17,11 @@ pub struct ProfileDetails {
 | 
			
		|||
    pub description: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct SessionDetails {
 | 
			
		||||
    pub did: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Bluesky {
 | 
			
		||||
    client: AtpServiceClient<AtpServiceWrapper<ReqwestClient>>,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -25,6 +33,77 @@ impl Bluesky {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn login(&self, handle: &str, password: &str) -> Result<SessionDetails> {
 | 
			
		||||
        use atrium_api::com::atproto::server::create_session::Input;
 | 
			
		||||
 | 
			
		||||
        let result = self
 | 
			
		||||
            .client
 | 
			
		||||
            .service
 | 
			
		||||
            .com
 | 
			
		||||
            .atproto
 | 
			
		||||
            .server
 | 
			
		||||
            .create_session(Input {
 | 
			
		||||
                identifier: handle.to_owned(),
 | 
			
		||||
                password: password.to_owned(),
 | 
			
		||||
            })
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        Ok(SessionDetails { did: result.did })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn upload_blob(&self, blob: Vec<u8>) -> Result<BlobRef> {
 | 
			
		||||
        let result = self
 | 
			
		||||
            .client
 | 
			
		||||
            .service
 | 
			
		||||
            .com
 | 
			
		||||
            .atproto
 | 
			
		||||
            .repo
 | 
			
		||||
            .upload_blob(blob)
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        Ok(result.blob)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn publish_feed(
 | 
			
		||||
        &self,
 | 
			
		||||
        publisher_did: &str,
 | 
			
		||||
        feed_generator_did: &str,
 | 
			
		||||
        name: &str,
 | 
			
		||||
        display_name: &str,
 | 
			
		||||
        description: &str,
 | 
			
		||||
        avatar: Option<BlobRef>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        use atrium_api::com::atproto::repo::put_record::Input;
 | 
			
		||||
 | 
			
		||||
        self.client
 | 
			
		||||
            .service
 | 
			
		||||
            .com
 | 
			
		||||
            .atproto
 | 
			
		||||
            .repo
 | 
			
		||||
            .put_record(Input {
 | 
			
		||||
                collection: "app.bsky.feed.generator".to_owned(),
 | 
			
		||||
                record: Record::AppBskyFeedGenerator(Box::new(
 | 
			
		||||
                    atrium_api::app::bsky::feed::generator::Record {
 | 
			
		||||
                        avatar,
 | 
			
		||||
                        created_at: Utc::now().to_string(),
 | 
			
		||||
                        description: Some(description.to_owned()),
 | 
			
		||||
                        description_facets: None,
 | 
			
		||||
                        did: feed_generator_did.to_owned(),
 | 
			
		||||
                        display_name: display_name.to_owned(),
 | 
			
		||||
                        labels: None,
 | 
			
		||||
                    },
 | 
			
		||||
                )),
 | 
			
		||||
                repo: publisher_did.to_owned(),
 | 
			
		||||
                rkey: name.to_owned(),
 | 
			
		||||
                swap_commit: None,
 | 
			
		||||
                swap_record: None,
 | 
			
		||||
                validate: None,
 | 
			
		||||
            })
 | 
			
		||||
            .await?;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn fetch_profile_details(&self, did: &str) -> Result<ProfileDetails> {
 | 
			
		||||
        let result = self
 | 
			
		||||
            .client
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +120,7 @@ impl Bluesky {
 | 
			
		|||
            .await?;
 | 
			
		||||
 | 
			
		||||
        let profile = match result.value {
 | 
			
		||||
            atrium_api::records::Record::AppBskyActorProfile(profile) => profile,
 | 
			
		||||
            Record::AppBskyActorProfile(profile) => profile,
 | 
			
		||||
            _ => return Err(anyhow!("Big bad, no such profile")),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue