base64
次は、コマンドオプションで指定した文字をbase64に変換してmention
するコードを書きます。
これでプログラムの完成です。
まずはbase64
のパッケージを追加します。
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 = "*"
iso8601-timestamp = "0.2.10"
base64 = "*"
そして、src/main.rs
のmentionのところにdid
をbase64に変換するコードを書いていきます。
これらはサブオプションに設定します。
要点をまとめるとこんな感じです。
example
.command(
Command::new("mention")
.alias("m")
.action(c_mention)
.flag(
Flag::new("base", FlagType::String)
.description("base flag\n\t\t\t$ ai m syui.bsky.social -p text -b 123")
.alias("b"),
)
.flag(
Flag::new("egg", FlagType::Bool)
.description("egg flag\n\t\t\t$ ai m syui.bsky.social -e")
.alias("e"),
)
let did = token_toml(&"did");
let body = "/egg ".to_owned() + &encode(did.as_bytes());
-b
で変換する文字列を指定できるようにします。必ず-b "foo bar"
というようにダブルクオーテーションで囲ってください。-e
でdid
を取ってきて自動変換してmentionするようにします。
# 指定してた文字列をbase64にしてmentionする
$ ai m yui.syui.ai -b "did:plc:4hqjfn7m6n5hno3doamuhgef"
@yui.syui.ai /egg ZGlkOnBsYzo0aHFqZm43bTZuNWhubzNkb2FtdWhnZWY=
# 自分のdidをbase64にしてmentionする
$ ai m yui.syui.ai -e
@yui.syui.ai /egg ZGlkOnBsYzo0aHFqZm43bTZuNWhubzNkb2FtdWhnZWY=
では、全部のコードを書いていきます。
src/main.rs
pub mod data;
pub mod mention;
pub mod profile;
//pub mod ascii;
use seahorse::{App, Command, Context, Flag, FlagType};
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;
use crate::data::Profile;
use crate::data::token_toml;
//use crate::ascii::c_ascii;
extern crate base64;
use base64::encode;
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),
)
.command(
Command::new("profile")
.alias("p")
.action(c_profile),
)
.command(
Command::new("mention")
.alias("m")
.action(c_mention)
.flag(
Flag::new("post", FlagType::String)
.description("post flag\n\t\t\t$ ai m syui.bsky.social -p text")
.alias("p"),
)
.flag(
Flag::new("base", FlagType::String)
.description("base flag\n\t\t\t$ ai m syui.bsky.social -p text -b 123")
.alias("b"),
)
.flag(
Flag::new("egg", FlagType::Bool)
.description("egg flag\n\t\t\t$ ai m syui.bsky.social -e")
.alias("e"),
)
)
;
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();
}
fn profile(c: &Context) {
let m = c.args[0].to_string();
let h = async {
let str = profile::get_request(m.to_string()).await;
println!("{}",str);
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn c_profile(c: &Context) {
access_token().unwrap();
profile(c);
}
fn mention(c: &Context) {
let m = c.args[0].to_string();
let h = async {
let str = profile::get_request(m.to_string()).await;
let profile: Profile = serde_json::from_str(&str).unwrap();
let udid = profile.did;
let handle = profile.handle;
let at = "@".to_owned() + &handle;
let e = at.chars().count();
let s = 0;
if let Ok(base) = c.string_flag("base") {
let body = "/egg ".to_owned() + &encode(base.as_bytes());
let str = mention::post_request(body.to_string(), at.to_string(), udid.to_string(), s, e.try_into().unwrap()).await;
println!("{}",str);
}
if let Ok(post) = c.string_flag("post") {
let str = mention::post_request(post.to_string(), at.to_string(), udid.to_string(), s, e.try_into().unwrap()).await;
println!("{}",str);
}
if c.bool_flag("egg") {
let did = token_toml(&"did");
let body = "/egg ".to_owned() + &encode(did.as_bytes());
println!("{}", body);
let str = mention::post_request(body.to_string(), at.to_string(), udid.to_string(), s, e.try_into().unwrap()).await;
println!("{}",str);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn c_mention(c: &Context) {
access_token().unwrap();
mention(c);
}
//fn c_ascii_art(_c: &Context) {
// c_ascii();
//}
cargo build
できました。
これでmentionをyui.syui.ai
に指定して、-e
のオプションを使うと、自分のdidをbase64に自動変換して送ってくれます。
./target/debug/ai m yui.syui.ai -e
しかし、これではコマンドが実行しづらい。
このコマンドをどこにいても実行できるよう、binary、つまり、cargo build
するとできる./target/debug/ai
を$PATH
に置いてみます。
linux
$ echo $PATH|tr : '\n'
/usr/bin
/usr/local/bin
$ sudo cp -rf ./target/debug/ai /usr/local/bin/
$ ai -h
Name:
ai
Flags:
-h, --help : Show help
Commands:
b, bluesky :
l, login :
p, profile :
m, mention :
windows
$ENV:Path.Split(";")
C:\Users\syui\scoop\apps\rust\current\bin
cp ~/scoop/rust/current/bin/
ai -h
こんな感じでrust
で自分のコマンドを作って遊んでみましょう。