diff --git a/.config/ai/scpt b/.config/ai/scpt index 9cbca76..e55225e 160000 --- a/.config/ai/scpt +++ b/.config/ai/scpt @@ -1 +1 @@ -Subproject commit 9cbca76fc7846495894b254881cde1f7dc0ae363 +Subproject commit e55225eb57a05f38ccc7be58ce6b00279f4d636c diff --git a/.gitignore b/.gitignore index d122b4a..5a33697 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ Cargo.lock target -*.json +#*.json *.DS_Store **.DS_Store scpt/json/ diff --git a/README.md b/README.md index da289b5..f8d1be5 100644 --- a/README.md +++ b/README.md @@ -107,3 +107,38 @@ ADMIN=syui.syu.is $ docker compose build $ docker compose up -d ``` + +## pds:card + +- https://atproto.com/ja/guides/lexicon +- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.card/3lagpwihqxi2v + +```sh +# oauth(button) +[yui]ai.syui.card.verify -> [user]ai.syui.card + +[yui] +$ ./target/debug/ai card-verify -i 0 -p 0 -r 0 -h syui.ai -d did:plc:uqzpqmrjnptsxezjx4xuh2mn +{"uri":"at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q"} + +[user] +$ ./target/debug/ai card -i 0 -p 0 -r 0 -v at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q +``` + +## pds:game + +- https://atproto.com/ja/specs/record-key +- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.game/self + +```sh +# oauth(play) +[yui]ai.syui.game.user -> [user]ai.syui.game + +[account] +# https://at.syu.is/at/did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui + ## [rkey] + 1. echo $handle|cut -d . -f 1 + 2. $handle + 3. tid +``` + diff --git a/src/data.rs b/src/data.rs index c4a8505..f7835b4 100644 --- a/src/data.rs +++ b/src/data.rs @@ -107,6 +107,7 @@ pub struct BaseUrl { pub record_list: String, pub record_create: String, pub record_delete: String, + pub record_put: String, pub session_create: String, pub session_refresh: String, pub session_get: String, @@ -142,6 +143,7 @@ pub fn url(s: &str) -> String { let baseurl = BaseUrl { profile_get: "com.atproto.identity.resolveHandle".to_string(), thread_get: "app.bsky.feed.getPostThread".to_string(), + record_put: "com.atproto.repo.putRecord".to_string(), record_create: "com.atproto.repo.createRecord".to_string(), record_delete: "com.atproto.repo.deleteRecord".to_string(), describe: "com.atproto.repo.describeRepo".to_string(), @@ -173,6 +175,7 @@ pub fn url(s: &str) -> String { "record_list" => t.to_string() + &baseurl.record_list, "record_create" => t.to_string() + &baseurl.record_create, "record_delete" => t.to_string() + &baseurl.record_delete, + "record_put" => t.to_string() + &baseurl.record_put, "session_create" => t.to_string() + &baseurl.session_create, "session_refresh" => t.to_string() + &baseurl.session_refresh, "session_get" => t.to_string() + &baseurl.session_get, diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..297cddc --- /dev/null +++ b/src/game.rs @@ -0,0 +1,5 @@ +pub mod post_card; +pub mod post_card_verify; +pub mod post_game; +pub mod post_game_user; +pub mod post_game_login; diff --git a/src/game/post_card.rs b/src/game/post_card.rs new file mode 100644 index 0000000..d3e4e34 --- /dev/null +++ b/src/game/post_card.rs @@ -0,0 +1,44 @@ +extern crate reqwest; +use crate::data_toml; +use crate::data_refresh; +use crate::url; +use iso8601_timestamp::Timestamp; +use serde_json::json; + +pub async fn post_request(verify: String, id: i32, cp: i32, rank: i32, rare: String, col: String, author: String) -> String { + let token = data_refresh(&"access"); + let did = data_toml(&"did"); + let handle = data_toml(&"handle"); + let url = url(&"record_create"); + let d = Timestamp::now_utc(); + let d = d.to_string(); + + let post = Some(json!({ + "repo": handle.to_string(), + "did": did.to_string(), + "collection": col.to_string(), + "record": { + "id": id, + "cp": cp, + "rank": rank, + "rare": rare.to_string(), + "author": author.to_string(), + "verify": verify.to_string(), + "createdAt": d.to_string(), + }, + })); + + let client = reqwest::Client::new(); + let res = client + .post(url) + .json(&post) + .header("Authorization", "Bearer ".to_owned() + &token) + .send() + .await + .unwrap() + .text() + .await + .unwrap(); + + return res; +} diff --git a/src/game/post_card_verify.rs b/src/game/post_card_verify.rs new file mode 100644 index 0000000..5543735 --- /dev/null +++ b/src/game/post_card_verify.rs @@ -0,0 +1,58 @@ +extern crate reqwest; +use crate::data_toml; +use crate::data_refresh; +use crate::url; +use iso8601_timestamp::Timestamp; +use serde_json::json; + +pub async fn post_request(col: String, img: String, id: i32, cp: i32, rank: i32, rare: String, user_handle: String, user_did: String) -> String { + let token = data_refresh(&"access"); + let did = data_toml(&"did"); + let handle = data_toml(&"handle"); + let url = url(&"record_create"); + let d = Timestamp::now_utc(); + let d = d.to_string(); + let link = "https://bsky.app/profile/yui.syui.ai".to_string(); + let post = Some(json!({ + "repo": handle.to_string(), + "did": did.to_string(), + "collection": col.to_string(), + "record": { + "id": id, + "cp": cp, + "rank": rank, + "rare": rare.to_string(), + "handle": user_handle.to_string(), + "did": user_did.to_string(), + "embed": { + "$type": "app.bsky.embed.external", + "external": { + "uri": link, + "thumb": { + "$type": "blob", + "ref": { + "$link": img.to_string() + }, + "mimeType": "image/jpeg", + "size": 0 + } + } + }, + "createdAt": d.to_string(), + }, + })); + + let client = reqwest::Client::new(); + let res = client + .post(url) + .json(&post) + .header("Authorization", "Bearer ".to_owned() + &token) + .send() + .await + .unwrap() + .text() + .await + .unwrap(); + + return res; +} diff --git a/src/game/post_game.rs b/src/game/post_game.rs new file mode 100644 index 0000000..332a023 --- /dev/null +++ b/src/game/post_game.rs @@ -0,0 +1,39 @@ +extern crate reqwest; +use crate::data_toml; +use crate::data_refresh; +use crate::url; +use iso8601_timestamp::Timestamp; +use serde_json::json; + +pub async fn post_request(col: String, account: String) -> String { + let token = data_refresh(&"access"); + let did = data_toml(&"did"); + let handle = data_toml(&"handle"); + let url = url(&"record_put"); + let d = Timestamp::now_utc(); + let d = d.to_string(); + let post = Some(json!({ + "repo": handle.to_string(), + "did": did.to_string(), + "collection": col.to_string(), + "rkey": "self".to_string(), + "record": { + "account": account.to_string(), + "createdAt": d.to_string(), + }, + })); + + let client = reqwest::Client::new(); + let res = client + .post(url) + .json(&post) + .header("Authorization", "Bearer ".to_owned() + &token) + .send() + .await + .unwrap() + .text() + .await + .unwrap(); + + return res; +} diff --git a/src/game/post_game_login.rs b/src/game/post_game_login.rs new file mode 100644 index 0000000..f5b04b2 --- /dev/null +++ b/src/game/post_game_login.rs @@ -0,0 +1,42 @@ +extern crate reqwest; +use crate::data_toml; +use crate::data_refresh; +use crate::url; +use iso8601_timestamp::Timestamp; +use serde_json::json; + +pub async fn post_request(col: String, username: String, login: bool, account: String) -> String { + let token = data_refresh(&"access"); + let did = data_toml(&"did"); + let handle = data_toml(&"handle"); + let url = url(&"record_put"); + let d = Timestamp::now_utc(); + let d = d.to_string(); + + let post = Some(json!({ + "repo": handle.to_string(), + "did": did.to_string(), + "collection": col.to_string(), + "rkey": "self".to_string(), + "record": { + "login": login, + "username": username.to_string(), + "account": account.to_string(), + "createdAt": d.to_string(), + }, + })); + + let client = reqwest::Client::new(); + let res = client + .post(url) + .json(&post) + .header("Authorization", "Bearer ".to_owned() + &token) + .send() + .await + .unwrap() + .text() + .await + .unwrap(); + + return res; +} diff --git a/src/game/post_game_user.rs b/src/game/post_game_user.rs new file mode 100644 index 0000000..0720c5c --- /dev/null +++ b/src/game/post_game_user.rs @@ -0,0 +1,55 @@ +extern crate reqwest; +use crate::data_toml; +use crate::data_refresh; +use crate::url; +use iso8601_timestamp::Timestamp; +use serde_json::json; + +pub async fn post_request(col: String, user_name: String, user_did: String, user_handle: String, aiten: i32, limit: i32, chara: String, lv: i32, exp: i32, hp: i32, rank: i32, mode: i32, attach: i32, critical: i32, critical_d: i32) -> String { + let token = data_refresh(&"access"); + let did = data_toml(&"did"); + let handle = data_toml(&"handle"); + let url = url(&"record_put"); + let d = Timestamp::now_utc(); + let d = d.to_string(); + let post = Some(json!({ + "repo": handle.to_string(), + "did": did.to_string(), + "collection": col.to_string(), + "rkey": user_name.to_string(), + "record": { + "did": user_did.to_string(), + "handle": user_handle.to_string(), + "aiten": aiten, + "limit": limit, + "character": { + chara.to_string(): { + "lv": lv, + "exp": exp, + "hp": hp, + "rank": rank, + "mode": mode, + "attach": attach, + "critical": critical, + "critical_d": critical_d, + } + }, + "createdAt": d.to_string(), + "updatedAt": d.to_string(), + }, + })); + + let client = reqwest::Client::new(); + let res = client + .post(url) + .json(&post) + .header("Authorization", "Bearer ".to_owned() + &token) + .send() + .await + .unwrap() + .text() + .await + .unwrap(); + + return res; +} diff --git a/src/main.rs b/src/main.rs index 7ccf663..102cc45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,11 @@ use crate::data::url; use crate::data::w_cfg; use crate::data::w_refresh; use crate::feed_watch::c_feed_watch; +use crate::game::post_card; +use crate::game::post_card_verify; +use crate::game::post_game; +use crate::game::post_game_user; +use crate::game::post_game_login; use data::ProfileIdentityResolve; @@ -30,6 +35,7 @@ pub mod notify_read; pub mod openai; pub mod post; pub mod post_link; +pub mod game; pub mod profile; pub mod refresh; pub mod reply; @@ -154,6 +160,160 @@ fn main() { .alias("c"), ) ) + .command( + Command::new("card") + .description("-v -i -p -r -c -a -img -rare ") + .action(card) + .flag( + Flag::new("id", FlagType::Int) + .alias("i"), + ) + .flag( + Flag::new("cp", FlagType::Int) + .alias("p"), + ) + .flag( + Flag::new("rank", FlagType::Int) + .alias("r"), + ) + .flag( + Flag::new("rare", FlagType::Int) + ) + .flag( + Flag::new("col", FlagType::String) + .alias("c"), + ) + .flag( + Flag::new("author", FlagType::String) + .alias("a"), + ) + .flag( + Flag::new("verify", FlagType::String) + .alias("v"), + ) + .flag( + Flag::new("img", FlagType::String) + ) + ) + .command( + Command::new("card-verify") + .description(" -c -i -p -r -rare -H -d ") + .action(card_verify) + .flag( + Flag::new("col", FlagType::String) + .alias("c"), + ) + .flag( + Flag::new("id", FlagType::Int) + .alias("i"), + ) + .flag( + Flag::new("cp", FlagType::Int) + .alias("p"), + ) + .flag( + Flag::new("rank", FlagType::Int) + .alias("r"), + ) + .flag( + Flag::new("rare", FlagType::String) + ) + .flag( + Flag::new("handle", FlagType::String) + .alias("H"), + ) + .flag( + Flag::new("did", FlagType::String) + .alias("did"), + ) + ) + .command( + Command::new("game") + .description("a ") + .action(game) + .flag( + Flag::new("col", FlagType::String) + .alias("c"), + ) + .flag( + Flag::new("account", FlagType::String) + .alias("a"), + ) + ) + .command( + Command::new("game-login") + .description("l -u -c ") + .action(game_login) + .flag( + Flag::new("col", FlagType::String) + .alias("c"), + ) + .flag( + Flag::new("login", FlagType::Bool) + .alias("l"), + ) + .flag( + Flag::new("username", FlagType::String) + .alias("u"), + ) + ) + .command( + Command::new("game-user") + .description("-chara ai -l 20240101 -ten 0 --lv 0 --exp 0 --hp 0 --rank 0 --mode 0 --attach 0 --critical 0 --critical_d 0") + .action(game_user) + .flag( + Flag::new("username", FlagType::String) + .alias("u"), + ) + .flag( + Flag::new("col", FlagType::String) + .alias("c"), + ) + .flag( + Flag::new("did", FlagType::String) + .alias("d"), + ) + .flag( + Flag::new("handle", FlagType::String) + .alias("H"), + ) + .flag( + Flag::new("character", FlagType::String) + .alias("chara"), + ) + .flag( + Flag::new("aiten", FlagType::Int) + .alias("ten"), + ) + .flag( + Flag::new("limit", FlagType::Int) + .alias("l"), + ) + .flag( + Flag::new("lv", FlagType::Int) + ) + .flag( + Flag::new("hp", FlagType::Int) + ) + .flag( + Flag::new("attach", FlagType::Int) + ) + .flag( + Flag::new("exp", FlagType::Int) + ) + .flag( + Flag::new("critical", FlagType::Int) + ) + .flag( + Flag::new("critical_d", FlagType::Int) + ) + .flag( + Flag::new("rank", FlagType::Int) + ) + .flag( + Flag::new("mode", FlagType::Int) + ) + ) .command( Command::new("like") .description("like -u ") @@ -234,6 +394,10 @@ fn main() { Flag::new("post", FlagType::String) .alias("p"), ) + .flag( + Flag::new("col", FlagType::String) + .alias("c"), + ) ) .command( Command::new("follow") @@ -474,6 +638,145 @@ fn like(c: &Context) { return res; } +async fn c_card(c: &Context) -> Result<(), Box> { + //let m = c.args[0].to_string(); + let author = c.string_flag("author").unwrap_or_else(|_| "syui".to_string()); + let verify = c.string_flag("verify").unwrap_or_else(|_| "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q".to_string()); + let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.card".to_string()); + //let img = c.string_flag("img").unwrap_or_else(|_| "bafkreigvcjc46qtelpc4wsg7fwf6qktbi6a23ouqiupth2r37zhrn7wbza".to_string()); + let id = c.int_flag("id")?.try_into()?; + let cp = c.int_flag("cp")?.try_into()?; + let rank = c.int_flag("rank")?.try_into()?; + let rare = c.string_flag("rare").unwrap_or_else(|_| "normal".to_string()); + let str = post_card::post_request(verify, id, cp, rank, rare, col, author); + println!("{}", str.await); + Ok(()) +} + +fn card(c: &Context) { + refresh(c); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + if let Err(e) = c_card(c).await { + eprintln!("Error: {}", e); + } + }); +} + +async fn c_card_verify(c: &Context) -> Result<(), Box> { + let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.card.verify".to_string()); + let img = c.string_flag("img").unwrap_or_else(|_| "bafkreigvcjc46qtelpc4wsg7fwf6qktbi6a23ouqiupth2r37zhrn7wbza".to_string()); + let id = c.int_flag("id")?.try_into()?; + let cp = c.int_flag("cp")?.try_into()?; + let rank = c.int_flag("rank")?.try_into()?; + let rare = c.string_flag("rare").unwrap_or_else(|_| "normal".to_string()); + let user_handle = c.string_flag("handle").unwrap_or_else(|_| "syui.ai".to_string()); + let user_did = c.string_flag("did").unwrap_or_else(|_| "did:plc:uqzpqmrjnptsxezjx4xuh2mn".to_string()); + + //match id === 1 let img = "xxx"; + let str = post_card_verify::post_request(col, img, id, cp, rank, rare, user_handle, user_did); + println!("{}", str.await); + Ok(()) +} + +fn card_verify(c: &Context) { + refresh(c); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + if let Err(e) = c_card_verify(c).await { + eprintln!("Error: {}", e); + } + }); +} + +async fn c_game(c: &Context) -> Result<(), Box> { + let account = c.string_flag("account").unwrap_or_else(|_| "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui".to_string()); + let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game".to_string()); + let handle = data_toml(&"handle"); + if handle == "syui.ai" { + let str = post_game::post_request(col, account); + println!("{}", str.await); + Ok(()) + } else { + Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized"))) + } +} + +fn game(c: &Context) { + refresh(c); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + if let Err(e) = c_game(c).await { + eprintln!("Error: {}", e); + } + }); +} + +async fn c_game_user(c: &Context) -> Result<(), Box> { + let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game.user".to_string()); + let user_name = c.string_flag("username").unwrap_or_else(|_| "syui".to_string()); + let user_handle = c.string_flag("handle").unwrap_or_else(|_| "syui.ai".to_string()); + let user_did = c.string_flag("did").unwrap_or_else(|_| "did:plc:uqzpqmrjnptsxezjx4xuh2mn".to_string()); + let chara = c.string_flag("character").unwrap_or_else(|_| "ai".to_string()); + let limit = c.int_flag("limit")?.try_into()?; + let aiten = c.int_flag("aiten")?.try_into()?; + let lv = c.int_flag("lv")?.try_into()?; + let exp = c.int_flag("exp")?.try_into()?; + let hp = c.int_flag("hp")?.try_into()?; + let rank = c.int_flag("rank")?.try_into()?; + let mode = c.int_flag("mode")?.try_into()?; + let attach = c.int_flag("attach")?.try_into()?; + let critical = c.int_flag("critical")?.try_into()?; + let critical_d = c.int_flag("critical_d")?.try_into()?; + + if data_toml(&"handle") == "yui.syui.ai" { + let str = post_game_user::post_request(col, user_name, user_did, user_handle, aiten, limit, chara, lv, exp, hp, rank, mode, attach, critical, critical_d); + println!("{}", str.await); + Ok(()) + } else { + Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized"))) + } +} + +fn game_user(c: &Context) { + refresh(c); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + if let Err(e) = c_game_user(c).await { + eprintln!("Error: {}", e); + } + }); +} + +async fn c_game_login(c: &Context) -> Result<(), Box> { + let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game.login".to_string()); + let user_name = c.string_flag("username").unwrap_or_else(|_| "syui".to_string()); + let account = "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/".to_string() + &user_name; + let login = c.bool_flag("login"); + if data_toml(&"handle") == "yui.syui.ai" { + let str = post_game_login::post_request(col, user_name, login, account); + println!("{}", str.await); + Ok(()) + } else { + Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized"))) + } +} + +fn game_login(c: &Context) { + refresh(c); + tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + if let Err(e) = c_game_login(c).await { + eprintln!("Error: {}", e); + } + }); +} + fn repost(c: &Context) { refresh(c); let m = c.args[0].to_string(); @@ -528,6 +831,7 @@ fn mention(c: &Context) { let h = async { let str = profile::get_request(m.to_string()).await; let profile: ProfileIdentityResolve = serde_json::from_str(&str).unwrap(); + let col = c.string_flag("col").unwrap_or_else(|_| "app.bsky.feed.post".to_string()); let udid = profile.did; let handle = m.to_string(); let at = "@".to_owned() + &handle; @@ -535,6 +839,7 @@ fn mention(c: &Context) { let s = 0; if let Ok(post) = c.string_flag("post") { let str = mention::post_request( + col, post.to_string(), at.to_string(), udid.to_string(), diff --git a/src/mention.rs b/src/mention.rs index 84cf978..4492650 100644 --- a/src/mention.rs +++ b/src/mention.rs @@ -5,13 +5,13 @@ use crate::url; use iso8601_timestamp::Timestamp; use serde_json::json; -pub async fn post_request(text: String, at: String, udid: String, s: i32, e: i32) -> String { +pub async fn post_request(col: String, text: String, at: String, udid: String, s: i32, e: i32) -> String { let token = data_refresh(&"access"); let did = data_toml(&"did"); let handle = data_toml(&"handle"); let url = url(&"record_create"); - let col = "app.bsky.feed.post".to_string(); + //let col = "app.bsky.feed.post".to_string(); let d = Timestamp::now_utc(); let d = d.to_string(); @@ -22,7 +22,7 @@ pub async fn post_request(text: String, at: String, udid: String, s: i32, e: i32 "collection": col.to_string(), "record": { "text": at.to_string() + &" ".to_string() + &text.to_string(), - "$type": "app.bsky.feed.post", + "$type": col.to_string(), "createdAt": d.to_string(), "facets": [ {