config
Add the code for bluesky's authentication system.
Specifically, write information in ~/.config/ai/config.toml
and create a command option to put authentication information in ~/.config/ai/token.toml
.
~/.config/ai/config.toml
handle = "yui.syui.ai"
pass = "xxx"
host = "bsky.social"
Cargo.toml
[package]
name = "ai"
version = "0.1.0"
edition = "2021"
[dependencies]
seahorse = "*"
reqwest = { version = "*", features = ["blocking", "json"] }
tokio = { version = "1", features = ["full"] }
serde_derive = "1.0"
serde_json = "1.0"
serde = "*"
config = { git = "https://github.com/mehcode/config-rs", branch = "master" }
shellexpand = "*"
toml = "*"
src/data.rs
use config::{Config, ConfigError, File};
use serde_derive::{Deserialize, Serialize};
#[derive(Debug, Deserialize)]
#[allow(unused)]
pub struct Data {
pub host: String,
pub pass: String,
pub handle: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Token {
pub did: String,
pub accessJwt: String,
pub refreshJwt: String,
pub handle: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Tokens {
pub did: String,
pub access: String,
pub refresh: String,
pub handle: String,
}
impl Data {
pub fn new() -> Result<Self, ConfigError> {
let d = shellexpand::tilde("~") + "/.config/ai/config.toml";
let s = Config::builder()
.add_source(File::with_name(&d))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
s.try_deserialize()
}
}
src/main.rs
pub mod data;
use seahorse::{App, Context, Command};
use std::env;
use std::fs;
use std::io::Write;
use std::collections::HashMap;
use data::Data as Datas;
use crate::data::Token;
use crate::data::Tokens;
fn main() {
let args: Vec<String> = env::args().collect();
let app = App::new(env!("CARGO_PKG_NAME"))
//.action(c_ascii_art)
.command(
Command::new("bluesky")
.alias("b")
.action(c_list_records),
)
.command(
Command::new("login")
.alias("l")
.action(c_access_token),
)
;
app.run(args);
}
#[tokio::main]
async fn list_records() -> reqwest::Result<()> {
let client = reqwest::Client::new();
let handle= "support.bsky.team";
let col = "app.bsky.feed.post";
let body = client.get("https://bsky.social/xrpc/com.atproto.repo.listRecords")
.query(&[("repo", &handle),("collection", &col),("limit", &"1"),("revert", &"true")])
.send()
.await?
.text()
.await?;
println!("{}", body);
Ok(())
}
fn c_list_records(_c: &Context) {
list_records().unwrap();
}
#[tokio::main]
async fn access_token() -> reqwest::Result<()> {
let file = "/.config/ai/token.toml";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
let data = Datas::new().unwrap();
let data = Datas {
host: data.host,
handle: data.handle,
pass: data.pass,
};
let url = "https://".to_owned() + &data.host + &"/xrpc/com.atproto.server.createSession";
let mut map = HashMap::new();
map.insert("identifier", &data.handle);
map.insert("password", &data.pass);
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&map)
.send()
.await?
.text()
.await?;
let json: Token = serde_json::from_str(&res).unwrap();
let tokens = Tokens {
did: json.did.to_string(),
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
handle: json.handle.to_string(),
};
let toml = toml::to_string(&tokens).unwrap();
let mut f = fs::File::create(f.clone()).unwrap();
f.write_all(&toml.as_bytes()).unwrap();
Ok(())
}
fn c_access_token(_c: &Context) {
access_token().unwrap();
}