1
0
This commit is contained in:
syui 2023-10-20 23:31:16 +09:00
parent 430b5977d8
commit 6a0b4582ff
Signed by: syui
GPG Key ID: 5417CFEBAD92DF56
20 changed files with 1103 additions and 1 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
Cargo.lock
target
*.json
*.DS_Store
**.DS_Store
scpt/*.json

18
Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "ai"
version = "0.0.1"
edition = "2021"
[dependencies]
seahorse = "*"
reqwest = { version = "*", features = ["blocking", "json"] }
tokio = { version = "1", features = ["full"] }
shellexpand = "*"
config = "*"
serde = "*"
serde_json = "*"
serde_derive = "*"
url = { version = "2.0", features = ["serde"] }
rustc-serialize = "*"
toml = "*"
iso8601-timestamp = "*"

View File

@ -1,6 +1,50 @@
## ai `bot` ## ai `bot`
<img src="./icon/avatar.png" width="100">
```sh
$ ai
```
```sh ```sh
$ cargo build $ cargo build
$ ./target/debug/ai $ ./target/debug/ai ai -t
``` ```
### login
```sh
# ai token $handle -p $password
$ ai t yui.syui.ai -p password
$ cat ~/.config/ai/token.toml
```
```sh
# ai token $handle -p $password -s $server
$ ai t ai.syu.is -p password -s syu.is
```
### refresh
```
$ ai r
```
```
# server: syu.is
$ ai r -s syu.is
```
### notify
```
$ ai n
```
### bot
```
$ ai bot
```

27
ai.zsh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
d=${0:a:h}/scpt
source $d/env
source $d/refresh.zsh
source $d/token.zsh
source $d/reply.zsh
source $d/notify.zsh
case $1 in
refresh|r)
refresh
;;
token|t)
token
;;
reply)
reply
;;
notify|n)
notify
;;
esac

BIN
icon/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
icon/avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 KiB

19
scpt/env Normal file
View File

@ -0,0 +1,19 @@
cfg=~/.config/ai/test.json
host=`cat $cfg|jq -r .host`
handle=`cat $cfg|jq -r .handle`
pass=`cat $cfg|jq -r .password`
date=`date --iso-8601=seconds`
if [ ! -f $cfg.t ];then
$d/token.zsh
fi
if [ -f $cfg.t ];then
token=`cat $cfg.t|jq -r .accessJwt`
refresh=`cat $cfg.t|jq -r .refreshJwt`
did=`cat $cfg.t|jq -r .did`
fi
if [ ! -d $d/json ];then
mkdir -p $d/json
fi

36
scpt/notify.zsh Normal file
View File

@ -0,0 +1,36 @@
function notify() {
url=https://$host/xrpc/app.bsky.notification.listNotifications
f=$d/json/notify.json
if [ ! -f $f ];then
curl -sL "Content-Type: application/json" -H "Authorization: Bearer $token" "$url?limit=100" >! $f
fi
for ((i=0;i<=99;i++))
do
cid=`cat $f|jq -r ".|.[].[$i]?|.cid?"`
uri=`cat $f|jq -r ".|.[].[$i]?|.uri?"`
echo cid: $cid
echo uri: $uri
cid_r=`cat $f|jq -r ".[]|.[$i]?|.record.reply.root.cid?"`
if [ "$cid_r" = "null" ];then
continue
fi
uri_r=`cat $f|jq -r ".[]|.[$i]?|.record.reply.root.uri?"`
cid_p=`cat $f|jq -r ".[]|.[$i]?|.record.reply.parent.cid?"`
uri_p=`cat $f|jq -r ".[]|.[$i]?|.record.reply.parent.uri?"`
did_p=`echo $uri_p|cut -d / -f 3`
if [ "$did_p" != "did:plc:uqzpqmrjnptsxezjx4xuh2mn" ];then
continue
fi
echo cid_root: $cid_r
echo uri_root: $uri_r
echo cid_parent: $cid_p
echo uri_parent: $uri_p
echo ---
echo $uri|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo $uri_r|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo $uri_p|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo ---
done
}

11
scpt/refresh.zsh Executable file
View File

@ -0,0 +1,11 @@
function refresh(){
token=`cat $cfg.t|jq -r .accessJwt`
refresh=`cat $cfg.t|jq -r .refreshJwt`
if [ ! -f $cfg ];then
token
fi
url=https://$host/xrpc/com.atproto.server.refreshSession
j=`curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $refresh" $url`
echo $j
echo $j >! $cfg.t
}

29
scpt/reply.zsh Executable file
View File

@ -0,0 +1,29 @@
function reply() {
url="https://$host/xrpc/com.atproto.repo.createRecord"
col="app.bsky.feed.post"
json="{
\"repo\": \"$handle\",
\"did\": \"$did\",
\"collection\": \"$col\",
\"record\": {
\"text\": \"$text\",
\"createdAt\": \"$date\",
\"reply\": {
\"root\": {
\"cid\": \"$cid\",
\"uri\": \"$uri\"
},
\"parent\": {
\"cid\": \"$cid_p\",
\"uri\": \"$uri_p\"
}
}
}
}"
echo $json|jq .
url=https://$host/xrpc/com.atproto.repo.createRecord
j=`curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d "$json" $url`
}

6
scpt/token.zsh Executable file
View File

@ -0,0 +1,6 @@
function token() {
url=https://$host/xrpc/com.atproto.server.createSession
j=`curl -sL -X POST -H "Content-Type: application/json" -d "{\"identifier\":\"$handle\",\"password\":\"$pass\"}" $url`
echo $j
echo $j >! $cfg.t
}

80
src/ascii.rs Normal file
View File

@ -0,0 +1,80 @@
pub fn c_ascii(x: bool) {
let logo = "
";
let avatar = "
";
match x {
true => println!("{}", avatar),
false => println!("{}", logo),
}
}

435
src/data.rs Normal file
View File

@ -0,0 +1,435 @@
use config::{Config, ConfigError, File};
use serde_derive::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
pub fn data_file(s: &str) -> String {
let file = "/.config/ai/token";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
match &*s {
"toml" => f + &".toml",
"json" => f + &".json",
_ => f + &"." + &s,
}
}
impl Token {
pub fn new() -> Result<Self, ConfigError> {
let d = data_file("json");
let s = Config::builder()
.add_source(File::with_name(&d))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
s.try_deserialize()
}
}
impl Data {
pub fn new() -> Result<Self, ConfigError> {
let d = data_file("toml");
let s = Config::builder()
.add_source(File::with_name(&d))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
s.try_deserialize()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Token {
pub did: String,
pub handle: String,
pub accessJwt: String,
pub refreshJwt: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Data {
pub host: String,
pub did: String,
pub handle: String,
pub access: String,
pub refresh: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct BaseUrl {
pub profile_get: String,
pub thread_get: String,
pub describe: String,
pub record_list: String,
pub record_create: String,
pub record_delete: String,
pub session_create: String,
pub session_refresh: String,
pub session_get: String,
pub timeline_get: String,
pub timeline_author: String,
pub upload_blob: String,
pub update_handle: String,
pub account_create: String,
pub notify_count: String,
pub notify_list: String,
pub notify_update: String,
pub repo_update: String,
pub like: String,
pub repost: String,
pub follow: String,
pub follows: String,
pub followers: String,
}
pub fn url(s: &str) -> String {
let s = String::from(s);
let data = Data::new().unwrap();
let data = Data {
host: data.host,
handle: data.handle,
did: data.did,
access: data.access,
refresh: data.refresh,
};
let t = "https://".to_string() + &data.host.to_string() + &"/xrpc/".to_string();
let baseurl = BaseUrl {
profile_get: "com.atproto.identity.resolveHandle".to_string(),
thread_get: "app.bsky.feed.getPostThread".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(),
record_list: "com.atproto.repo.listRecords".to_string(),
session_create: "com.atproto.server.createSession".to_string(),
session_refresh: "com.atproto.server.refreshSession".to_string(),
session_get: "com.atproto.server.getSession".to_string(),
timeline_get: "app.bsky.feed.getTimeline".to_string(),
timeline_author: "app.bsky.feed.getAuthorFeed".to_string(),
like: "app.bsky.feed.like".to_string(),
repost: "app.bsky.feed.repost".to_string(),
follow: "app.bsky.graph.follow".to_string(),
follows: "app.bsky.graph.getFollows".to_string(),
followers: "app.bsky.graph.getFollowers".to_string(),
upload_blob: "com.atproto.repo.uploadBlob".to_string(),
account_create: "com.atproto.server.createAccount".to_string(),
update_handle: "com.atproto.identity.updateHandle".to_string(),
notify_count: "app.bsky.notification.getUnreadCount".to_string(),
notify_list: "app.bsky.notification.listNotifications".to_string(),
notify_update: "app.bsky.notification.updateSeen".to_string(),
repo_update: "com.atproto.sync.updateRepo".to_string(),
};
match &*s {
"profile_get" => t.to_string() + &baseurl.profile_get,
"thread_get" => t.to_string() + &baseurl.thread_get,
"describe" => t.to_string() + &baseurl.describe,
"record_list" => t.to_string() + &baseurl.record_list,
"record_create" => t.to_string() + &baseurl.record_create,
"record_delete" => t.to_string() + &baseurl.record_delete,
"session_create" => t.to_string() + &baseurl.session_create,
"session_refresh" => t.to_string() + &baseurl.session_refresh,
"session_get" => t.to_string() + &baseurl.session_get,
"timeline_get" => t.to_string() + &baseurl.timeline_get,
"timeline_author" => t.to_string() + &baseurl.timeline_get,
"upload_blob" => t.to_string() + &baseurl.upload_blob,
"account_create" => t.to_string() + &baseurl.account_create,
"update_handle" => t.to_string() + &baseurl.update_handle,
"notify_list" => t.to_string() + &baseurl.notify_list,
"notify_count" => t.to_string() + &baseurl.notify_count,
"notify_update" => t.to_string() + &baseurl.notify_update,
"repo_update" => t.to_string() + &baseurl.repo_update,
"like" => t.to_string() + &baseurl.like,
"repost" => t.to_string() + &baseurl.repost,
"follow" => t.to_string() + &baseurl.follow,
"follows" => t.to_string() + &baseurl.follows,
"followers" => t.to_string() + &baseurl.followers,
_ => s,
}
}
pub fn data_toml(s: &str) -> String {
let s = String::from(s);
let data = Data::new().unwrap();
let data = Data {
host: data.host,
handle: data.handle,
did: data.did,
access: data.access,
refresh: data.refresh,
};
match &*s {
"host" => data.handle,
"handle" => data.handle,
"did" => data.did,
"access" => data.access,
"refresh" => data.refresh,
_ => s,
}
}
pub fn w_cfg(h: &str, res: &str) {
let f = data_file(&"json");
let ff = data_file(&"toml");
let mut f = fs::File::create(f.clone()).unwrap();
let mut ff = fs::File::create(ff.clone()).unwrap();
f.write_all(&res.as_bytes()).unwrap();
let json: Token = serde_json::from_str(&res).unwrap();
let datas = Data {
host: h.to_string(),
did: json.did.to_string(),
handle: json.handle.to_string(),
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
};
let toml = toml::to_string(&datas).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
}
#[derive(Serialize, Deserialize)]
pub struct Notify {
pub notifications: Vec<Notifications>
}
#[derive(Serialize, Deserialize)]
pub struct Status {
pub handle: String,
pub did: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct DidDocs {
pub verificationMethod: Vec<VerificationMethod>,
pub service: Vec<Service>,
pub id: String,
pub alsoKnownAs: Vec<AlsoKnownAs>,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct VerificationMethod {
pub id: String,
pub r#type: String,
pub controller: String,
pub publicKeyMultibase: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Service {
pub id: String,
pub r#type: String,
pub serviceEndpoint: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct AlsoKnownAs {
}
#[derive(Serialize, Deserialize)]
pub struct Timeline {
pub feed: Vec<Feed>
}
#[derive(Serialize, Deserialize)]
pub struct Session {
pub did: String,
pub email: String,
pub handle: String,
}
#[derive(Serialize, Deserialize)]
pub struct Follow {
pub follows: Vec<Author>,
pub cursor: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Notifications {
pub uri: String,
pub cid: String,
pub author: Author,
pub reason: String,
//pub reasonSubject: String,
pub record: Record,
pub isRead: bool,
pub indexedAt: String,
//pub labels: Labels,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Thread {
pub r#type: String,
pub post: String,
pub root: String,
pub author: Author,
pub reason: String,
//pub reasonSubject: String,
pub record: Record,
pub isRead: bool,
pub indexedAt: String,
//pub labels: Labels,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Author {
pub did: String,
//pub declaration: Declaration,
pub description: Option<String>,
pub displayName: Option<String>,
pub handle: String,
pub avatar: Option<String>,
pub viewer: Viewer,
//pub labels: Labels,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Labels {
pub src: Option<String>,
pub uri: Option<String>,
pub cid: Option<String>,
pub val: Option<String>,
pub cts: Option<String>,
pub neg: Option<bool>,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Declaration {
pub actorType: String,
pub cid: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Viewer {
pub muted: bool,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct Record {
pub text: Option<String>,
pub createdAt: String,
pub reply: Option<Reply>,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct Reply {
pub parent: ReplyParent,
pub root: ReplyRoot,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct ReplyRoot {
pub cid: String,
pub uri: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
#[derive(Debug)]
pub struct ReplyParent {
pub cid: String,
pub uri: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Langs {
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Feed {
pub post: Post,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Post {
pub did: Option<String>,
pub uri: String,
pub cid: String,
pub collection: Option<String>,
pub record: Record,
pub author: Author,
pub reason: Option<String>,
pub indexedAt: String,
pub replyCount: i32,
pub postCount: Option<i32>,
pub repostCount: i32,
pub likeCount: i32,
}
#[derive(Serialize, Deserialize)]
pub struct Cid {
pub cid: String
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Img {
pub blob: Blob
}
#[derive(Serialize, Deserialize)]
pub struct Blob {
pub r#ref: Ref,
}
#[derive(Serialize, Deserialize)]
pub struct Ref {
pub link: String,
}
#[derive(Serialize, Deserialize)]
pub struct Handle {
pub handle: String
}
//#[derive(Serialize, Deserialize)]
//pub struct Did {
// pub did: String
//}
//#[derive(Serialize, Deserialize)]
//pub struct Labels {
//}
//
//#[derive(Serialize, Deserialize)]
//pub struct Viewer {
// pub muted: bool,
// pub blockedBy: bool,
//}
//
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct ProfileIdentityResolve {
pub did: String,
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct Profile {
pub did: String,
pub handle: String,
pub followsCount: Option<i32>,
pub followersCount: Option<i32>,
pub postsCount: i32,
pub indexedAt: Option<String>,
pub avatar: Option<String>,
pub banner: Option<String>,
pub displayName: Option<String>,
pub description: Option<String>,
pub viewer: Viewer,
pub labels: Labels,
}

168
src/main.rs Normal file
View File

@ -0,0 +1,168 @@
use seahorse::{App, Command, Context, Flag, FlagType};
use std::env;
use crate::ascii::c_ascii;
use crate::data::data_toml;
use crate::data::url;
use crate::data::w_cfg;
use data::Notify as Notify;
pub mod data;
pub mod refresh;
pub mod token;
pub mod notify;
pub mod notify_read;
pub mod reply;
pub mod reply_link;
pub mod ascii;
fn main() {
let args: Vec<String> = env::args().collect();
let app = App::new(env!("CARGO_PKG_NAME"))
.command(
Command::new("ai")
.alias("a")
.action(c_ascii_art)
.flag(
Flag::new("type", FlagType::Bool)
.description("type flag")
.alias("t"),
)
)
.command(
Command::new("token")
.alias("t")
.description("handle\n\t\t\t$ ai t syui.bsky.social -p password")
.action(c_token)
.flag(
Flag::new("password", FlagType::String)
.description("password flag")
.alias("p"),
)
.flag(
Flag::new("server", FlagType::String)
.description("server flag")
.alias("s"),
)
)
.command(
Command::new("refresh")
.alias("r")
.action(c_refresh)
.flag(
Flag::new("server", FlagType::String)
.description("server flag")
.alias("s"),
)
)
.command(
Command::new("notify")
.alias("n")
.action(c_notify),
)
.command(
Command::new("bot")
.alias("b")
.action(c_bot),
)
;
app.run(args);
}
fn c_ascii_art(c: &Context) {
c_ascii(c.bool_flag("type"));
}
fn token(c: &Context) {
let m = c.args[0].to_string();
let h = async {
if let Ok(p) = c.string_flag("password") {
if let Ok(s) = c.string_flag("server") {
let res = token::post_request(m.to_string(), p.to_string(), s.to_string()).await;
w_cfg(&s, &res)
} else {
let res = token::post_request(m.to_string(), p.to_string(), "bsky.social".to_string()).await;
w_cfg(&"bsky.social", &res)
}
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn c_token(c: &Context) {
token(c);
}
fn refresh(c: &Context) {
let h = async {
let res = refresh::post_request().await;
if let Ok(s) = c.string_flag("server") {
w_cfg(&s, &res)
} else {
w_cfg("bsky.social", &res)
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn c_refresh(c: &Context) {
refresh(c);
}
fn notify() {
let h = async {
let j = notify::get_request(100).await;
println!("{}", j);
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn c_notify(_c: &Context) {
notify();
}
pub fn char_c(i: String) -> String {
let l = 250;
let mut s = String::new();
for ii in i.chars().enumerate() {
match ii.0 {
n if n > l.try_into().unwrap() => {break}
_ => {s.push(ii.1)}
}
}
return s
}
fn bot(_c: &Context) {
let h = async {
let notify = notify::get_request(100).await;
let notify: Notify = serde_json::from_str(&notify).unwrap();
let n = notify.notifications;
let length = &n.len();
let reason = &n[0].reason;
let handle = &n[0].author.handle;
let did = &n[0].author.did;
let read = n[0].isRead;
let cid = &n[0].cid;
let uri = &n[0].uri;
println!("{}", length);
println!("{}", reason);
println!("{}", handle);
println!("{}", did);
println!("{}", read);
println!("{} {}", cid, uri);
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn c_bot(c: &Context) {
loop {
bot(c);
}
}

31
src/notify.rs Normal file
View File

@ -0,0 +1,31 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
//use serde_json::json;
pub async fn get_request(limit: i32, ) -> String {
let token = data_toml(&"access");
let url = url(&"notify_list");
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("limit", limit)])
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap();
let status_ref = res.error_for_status_ref();
match status_ref {
Ok(_) => {
return res.text().await.unwrap();
},
Err(_e) => {
let e = "err".to_string();
return e
}
}
}

28
src/notify_read.rs Normal file
View File

@ -0,0 +1,28 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
pub async fn post_request(time: String) -> String {
let token = data_toml(&"access");
let url = url(&"notify_update");
let post = Some(json!({
"seenAt": time.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
}

21
src/refresh.rs Normal file
View File

@ -0,0 +1,21 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
pub async fn post_request() -> String {
let refresh = data_toml(&"refresh");
let url = url(&"session_refresh");
let client = reqwest::Client::new();
let res = client
.post(url)
.header("Authorization", "Bearer ".to_owned() + &refresh)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

53
src/reply.rs Normal file
View File

@ -0,0 +1,53 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(text: String, cid: String, uri: String, cid_p: String, uri_p: String) -> String {
let token = data_toml(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
//let url = "https://bsky.social/xrpc/com.atproto.repo.createRecord";
let col = "app.bsky.feed.post".to_string();
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": {
"text": text.to_string(),
"createdAt": d.to_string(),
"reply": {
"root": {
"cid": cid.to_string(),
"uri": uri.to_string()
},
"parent": {
"cid": cid_p.to_string(),
"uri": uri_p.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
}

66
src/reply_link.rs Normal file
View File

@ -0,0 +1,66 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(text: String, link: String, s: i32, e: i32, cid: String, uri: String, cid_b: String, uri_b: String) -> String {
let token = data_toml(&"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 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": {
"text": link.to_string() + &" ".to_string() + &text.to_string(),
"createdAt": d.to_string(),
"reply": {
"root": {
"cid": cid.to_string(),
"uri": uri.to_string()
},
"parent": {
"cid": cid_b.to_string(),
"uri": uri_b.to_string()
}
},
"facets": [
{
"index": {
"byteStart": s,
"byteEnd": e
},
"features": [
{
"$type": "app.bsky.richtext.facet#link",
"uri": link.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
}

24
src/token.rs Normal file
View File

@ -0,0 +1,24 @@
extern crate reqwest;
use std::collections::HashMap;
pub async fn post_request(handle: String, pass: String, host: String) -> String {
let url = "https://".to_owned() + &host.to_string() + &"/xrpc/com.atproto.server.createSession".to_string();
let mut map = HashMap::new();
map.insert("identifier", &handle);
map.insert("password", &pass);
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&map)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}