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"というようにダブルクオーテーションで囲ってください。-edidを取ってきて自動変換して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で自分のコマンドを作って遊んでみましょう。

results matching ""

    No results matching ""