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

0
.config/keep Normal file

7
.gitignore vendored Normal file

@ -0,0 +1,7 @@
Cargo.lock
target
*.json
*.DS_Store
**.DS_Store
scpt/json/
.config/ai

19
Cargo.toml Normal file

@ -0,0 +1,19 @@
[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 = "*"
sysinfo = "*"

10
Dockerfile Normal file

@ -0,0 +1,10 @@
FROM syui/aios
USER root
ADD .config /root/.config
WORKDIR /root
ADD ./scpt/entrypoint.sh /
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

@ -1,6 +1,80 @@
## ai `bot`
<img src="./icon/avatar.png" width="100">
- name : ai bot
- base : [rust](https://www.rust-lang.org)
- host : [yui.syui.ai](https://bsky.app/profile/yui.syui.ai), [ai.syu.is](https://web.syu.is/profile/ai.syu.is)
```sh
$ ai
```
### logo
```sh
$ cargo build
$ ./target/debug/ai
$ ./target/debug/ai ai
```
```sh
$ ai ai -t avatar
```
### login
```sh
# ai login $handle -p $password
$ ai l yui.syui.ai -p password
$ cat ~/.config/ai/token.toml
```
```sh
# ai l $handle -p $password -s $server
$ ai l ai.syu.is -p password -s syu.is
```
### refresh
```
$ ai r
```
### notify
```
$ ai n
```
```
$ ai n | jq .
```
### bot
```
$ ai bot
```
|command|type|body|
|---|---|---|
|@yui.syui.ai did|mention, reply| plc.directory/$did/log |
### test
`zsh`
```sh
$ ./ai.zsh t
```
### docker
```sh
$ cp -rf ~/.config/ai ./config/
$ docker build -t syui/aios .
$ docker run -it syui/aios ai bot
---
$ docker stop `docker ps -a -q`
```

30
ai.zsh Executable file

@ -0,0 +1,30 @@
#!/bin/zsh
# https://www.docs.bsky.app/docs/get-started
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|rep)
reply
;;
notify|n)
notify
;;
esac

BIN
icon/ai.png Normal file

Binary file not shown.

After

(image error) Size: 89 KiB

BIN
icon/avatar.png Normal file

Binary file not shown.

After

(image error) Size: 557 KiB

3
scpt/entrypoint.sh Normal file

@ -0,0 +1,3 @@
#!/bin/bash
exec "$@"

34
scpt/env Normal file

@ -0,0 +1,34 @@
cfg=~/.config/ai/test.json
if [ ! -f $cfg ] || ! cat $cfg|jq . || [ "`cat $cfg|jq .host`" = "null" ] || [ -z "`cat $cfg`" ];then
mkdir -p ~/.config/ai
echo server:
read host
echo password:
read pass
echo handle:
read handle
echo "{ \"host\":\"$host\", \"password\":\"$pass\", \"handle\":\"$handle\" }" >> $cfg
fi
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

37
scpt/notify.zsh Normal file

@ -0,0 +1,37 @@
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
echo "[$i]---"
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: $uri|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo uri_root: $uri_r|sed "s#at://#https://bsky.app/profile/#g"|sed 's/app.bsky.feed.post/post/g'
echo uri_parent: $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

@ -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
}

40
scpt/reply.zsh Executable file

@ -0,0 +1,40 @@
function reply() {
#uri: https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef/post/3kkumyv72w22o
#uri_root: https://bsky.app/profile/did:plc:uqzpqmrjnptsxezjx4xuh2mn/post/3kkumysfipk2p
#uri_parent: https://bsky.app/profile/did:plc:uqzpqmrjnptsxezjx4xuh2mn/post/3kkumysfipk2p
cid=bafyreiaxz6hbqgylsxglqita73c5gzxzoatupgitd35rwjpd6dzpa4ctwi
uri=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kkumyv72w22o
cid_root=bafyreiacxuk4ypaxbg7qxnmrvpnaej5o7azewqioelfgbuikp77jevy6hq
uri_root=at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3kkumysfipk2p
cid_parent=bafyreiacxuk4ypaxbg7qxnmrvpnaej5o7azewqioelfgbuikp77jevy6hq
uri_parent=at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3kkumysfipk2p
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_root\",
\"uri\": \"$uri_root\"
},
\"parent\": {
\"cid\": \"$cid\",
\"uri\": \"$uri\"
}
}
}
}"
echo $json|jq .
url=https://$host/xrpc/com.atproto.repo.createRecord
curl -sL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $token" -d "$json" $url
}

6
scpt/token.zsh Executable 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
}

202
src/ascii.rs Normal file

@ -0,0 +1,202 @@
use sysinfo::{
System,
//Components, Disks, Networks, System,
};
pub fn c_ascii(x: &str) {
let logo = "
";
let avatar = "
";
let color = "








































";
let avatar_color = "








































";
let mut sys = System::new_all();
sys.refresh_all();
let s = x.to_string();
match &*s {
"logo" => println!("{}", logo),
"color" => println!("{}", color),
"avatar" => println!("{}", avatar),
"avatar_color" => println!("{}", avatar_color),
"os" => println!("{}\n\t", color),
_ => println!("not matched"),
}
println!("total memory: {} bytes", sys.total_memory());
println!("System name: {:?}", System::name().unwrap());
println!("System kernel version: {:?}", System::kernel_version().unwrap());
//println!("System OS version: {:?}", System::os_version().unwrap());
println!("System host name: {:?}", System::host_name().unwrap());
println!("NB CPUs: {}", sys.cpus().len());
//let disks = Disks::new_with_refreshed_list();
//for disk in &disks {
// println!("{disk:?}");
//}
//let networks = Networks::new_with_refreshed_list();
//println!("=> networks:");
//for (interface_name, data) in &networks {
// println!("{interface_name}: {}/{} B", data.received(), data.transmitted());
//}
//let components = Components::new_with_refreshed_list();
//println!("=> components:");
//for component in &components {
// println!("{component:?}");
//}
}

77
src/bot.rs Normal file

@ -0,0 +1,77 @@
use seahorse::Context;
use crate::refresh;
use crate::reply_link;
use crate::notify;
use crate::notify_read;
use crate::data::log_file;
use crate::data::w_cid;
use crate::data::c_char;
use crate::data::Notify as Notify;
pub fn c_bot(c: &Context) {
let h = async {
let mut notify = notify::get_request(100).await;
if notify == "err" {
refresh(c);
notify = notify::get_request(100).await;
}
let notify: Notify = serde_json::from_str(&notify).unwrap();
let n = notify.notifications;
let length = &n.len();
let su = 0..*length;
for i in su {
let reason = &n[i].reason;
let handle = &n[i].author.handle;
let did = &n[i].author.did;
let read = n[i].isRead;
let cid = &n[i].cid;
let uri = &n[i].uri;
let time = &n[i].indexedAt;
let mut cid_root = cid;
let mut uri_root = uri;
let check_cid = w_cid(cid.to_string(), log_file(&"n1"), false);
let check_cid_run = w_cid(cid.to_string(), log_file(&"n2"), false);
// thread
if ! n[i].record.reply.is_none() {
cid_root = &n[i].record.reply.as_ref().unwrap().root.cid;
uri_root = &n[i].record.reply.as_ref().unwrap().root.uri;
}
println!("{}", read);
println!("{}", handle);
println!("{} {}", cid, uri);
let mut text = "";
if ! n[i].record.text.is_none() {
text = &n[i].record.text.as_ref().unwrap();
}
let vec: Vec<&str> = text.split_whitespace().collect();
let rep_com = &vec[0..].join(" ");
if check_cid == false && { reason == "mention" || reason == "reply" } || check_cid_run == false && { reason == "mention" || reason == "reply" } {
w_cid(cid.to_string(), log_file(&"n2"), true);
if rep_com.contains("did") == true || rep_com.contains("/did") == true {
let link = "https://plc.directory/".to_owned() + &did + &"/log";
let s = 0;
let e = link.chars().count();
let d = "\n".to_owned() + &did.to_string();
let text_limit = c_char(d);
if text_limit.len() > 3 {
let str_rep = reply_link::post_request(text_limit.to_string(), link.to_string(), s, e.try_into().unwrap(), cid.to_string(), uri.to_string(), cid_root.to_string(), uri_root.to_string()).await;
let str_notify = notify_read::post_request(time.to_string()).await;
w_cid(cid.to_string(), log_file(&"n1"), true);
println!("{}", str_rep);
println!("{}", str_notify);
}
}
}
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}

554
src/data.rs Normal file

@ -0,0 +1,554 @@
use seahorse::Context;
use config::{Config, ConfigError, File};
use serde_derive::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
use std::io::Read;
use std::fs::OpenOptions;
use std::path::Path;
pub fn data_file(s: &str) -> String {
let file = "/.config/ai/token";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
let path = Path::new(&f);
if path.is_dir() == false {
let _ = fs::create_dir_all(f.clone());
}
match &*s {
"toml" => f + &".toml",
"json" => f + &".json",
_ => f + &"." + &s,
}
}
pub fn log_file(s: &str) -> String {
let file = "/.config/ai/txt/";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
let path = Path::new(&f);
if path.is_dir() == false {
let _ = fs::create_dir_all(f.clone());
}
match &*s {
"n1" => f + &"notify_cid.txt",
"n2" => f + &"notify_cid_run.txt",
_ => 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,
}
}
#[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,
}
pub fn c_char(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
}
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();
}
pub fn w_cid(cid :String, file: String, t: bool) -> bool {
let f = file;
let mut file = match OpenOptions::new()
.create(true)
.write(true)
.read(true)
.append(true)
.open(f.clone())
{
Err(why) => panic!("Couldn't open {}: {}", f, why),
Ok(file) => file,
};
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Err(why) => panic!("Couldn't read {}: {}", f, why),
Ok(_) => (),
}
if contents.contains(&cid) == false {
if t {
let cid = cid + "\n";
match file.write_all(cid.as_bytes()) {
Err(why) => panic!("Couldn't write \"{}\" to {}: {}", contents, f, why),
Ok(_) => println!("finished"),
}
}
let check = false;
return check
} else {
let check = true;
return check
}
}
pub fn c_follow_all() {
let file = "/.config/ai/scpt/follow_all.zsh";
let mut f = shellexpand::tilde("~").to_string();
f.push_str(&file);
use std::process::Command;
let output = Command::new(&f).output().expect("zsh");
let d = String::from_utf8_lossy(&output.stdout);
let d = "\n".to_owned() + &d.to_string();
println!("{}", d);
}
pub fn c_openai_key(c: &Context) {
let api = c.args[0].to_string();
let o = "api='".to_owned() + &api.to_string() + &"'".to_owned();
let o = o.to_string();
let l = shellexpand::tilde("~") + "/.config/ai/openai.toml";
let l = l.to_string();
let mut l = fs::File::create(l).unwrap();
if o != "" {
l.write_all(&o.as_bytes()).unwrap();
}
println!("{:#?}", l);
}
impl Open {
pub fn new() -> Result<Self, ConfigError> {
let d = shellexpand::tilde("~") + "/.config/ai/openai.toml";
let s = Config::builder()
.add_source(File::with_name(&d))
.add_source(config::Environment::with_prefix("APP"))
.build()?;
s.try_deserialize()
}
}
#[derive(Debug, Deserialize)]
pub struct Open {
pub api: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct OpenData {
choices: Vec<Choices>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Choices {
text: String,
}

23
src/describe.rs Normal file

@ -0,0 +1,23 @@
extern crate reqwest;
//use crate::data_toml;
use crate::url;
pub async fn get_request(user: String) -> String {
//let token = data_toml(&"access");
let url = url(&"describe");
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("repo", &user)])
//.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

82
src/follow.rs Normal file

@ -0,0 +1,82 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
//use crate::data::Follow;
pub async fn post_request(u: 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.graph.follow".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": {
"subject": u.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
}
pub async fn delete_request(u: String, rkey: String) -> String {
let token = data_toml(&"access");
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_delete");
let col = "app.bsky.graph.follow".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(),
"rkey": rkey.to_string(),
"record": {
"subject": u.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
}

25
src/followers.rs Normal file

@ -0,0 +1,25 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
//use serde_json::json;
pub async fn get_request(actor: String, cursor: Option<String>) -> String {
let token = data_toml(&"access");
let url = url(&"followers");
let cursor = cursor.unwrap();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("actor", actor),("cursor", cursor)])
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

27
src/follows.rs Normal file

@ -0,0 +1,27 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
//use serde_json::json;
pub async fn get_request(actor: String, cursor: Option<String>) -> String {
let token = data_toml(&"access");
let url = url(&"follows");
let cursor = cursor.unwrap();
//let cursor = "1682386039125::bafyreihwgwozmvqxcxrhbr65agcaa4v357p27ccrhzkjf3mz5xiozjvzfa".to_string();
//let cursor = "1682385956974::bafyreihivhux5m3sxbg33yruhw5ozhahwspnuqdsysbo57smzgptdcluem".to_string();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("actor", actor),("cursor", cursor)])
//cursor.unwrap()
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

45
src/like.rs Normal file

@ -0,0 +1,45 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(cid: String, uri: 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.like".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": {
"subject": {
"uri": uri.to_string(),
"cid": cid.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
}

468
src/main.rs Normal file

@ -0,0 +1,468 @@
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 crate::data::c_follow_all;
use crate::bot::c_bot;
use crate::data::c_openai_key;
use data::ProfileIdentityResolve as ProfileIdentityResolve;
pub mod ascii;
pub mod data;
pub mod refresh;
pub mod token;
pub mod session;
pub mod notify;
pub mod notify_read;
pub mod reply;
pub mod reply_og;
pub mod reply_link;
pub mod describe;
pub mod timeline_author;
pub mod post;
pub mod post_link;
pub mod mention;
pub mod profile;
pub mod follow;
pub mod follows;
pub mod followers;
pub mod like;
pub mod repost;
pub mod bot;
pub mod openai;
fn main() {
let args: Vec<String> = env::args().collect();
let app = App::new(env!("CARGO_PKG_NAME"))
.command(
Command::new("ai")
.alias("a")
.action(ascii_art)
.flag(
Flag::new("type", FlagType::String)
.description("type flag")
.alias("t"),
)
)
.command(
Command::new("bot")
.alias("b")
.action(bot),
)
.command(
Command::new("follow_all")
.action(follow_all),
)
.command(
Command::new("login")
.alias("l")
.description("l <handle> -p <password>\n\t\t\tl <handle> -p <password> -s <server>")
.action(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(refresh),
)
.command(
Command::new("notify")
.alias("n")
.action(notify),
)
.command(
Command::new("timeline")
.alias("t")
.action(timeline),
)
.command(
Command::new("did")
.description("did <handle>")
.action(did)
)
.command(
Command::new("post")
.description("p <text>")
.alias("p")
.action(post)
.flag(
Flag::new("link", FlagType::String)
.alias("l"),
)
)
.command(
Command::new("like")
.description("like <cid> -u <uri>")
.action(like)
.flag(
Flag::new("uri", FlagType::String)
.alias("u"),
)
)
.command(
Command::new("repost")
.description("repost <cid> -u <uri>")
.action(repost)
.flag(
Flag::new("uri", FlagType::String)
.alias("u"),
)
)
.command(
Command::new("reply-og")
.description("reply-og <text> -c <cid> -u <uri> -i <img> -t <title> -d <description> -l <link>")
.action(reply_og)
.flag(
Flag::new("uri", FlagType::String)
.alias("u"),
)
.flag(
Flag::new("cid", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("link", FlagType::String)
.alias("l"),
)
.flag(
Flag::new("title", FlagType::String)
.alias("t"),
)
.flag(
Flag::new("description", FlagType::String)
.alias("d"),
)
.flag(
Flag::new("img", FlagType::String)
.alias("i"),
)
)
.command(
Command::new("reply")
.description("r <text> -u <uri> -c <cid>")
.action(reply)
.flag(
Flag::new("uri", FlagType::String)
.alias("u"),
)
.flag(
Flag::new("cid", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("link", FlagType::String)
.description("-l <link>")
.alias("l"),
)
)
.command(
Command::new("mention")
.description("@ <handle> -p <text>")
.alias("@")
.action(mention)
.flag(
Flag::new("post", FlagType::String)
.alias("p"),
)
)
.command(
Command::new("follow")
.description("follow <did>")
.action(follow)
.flag(
Flag::new("follows", FlagType::Bool)
.alias("s"),
)
.flag(
Flag::new("delete", FlagType::String)
.alias("d"),
)
.flag(
Flag::new("followers", FlagType::Bool)
.alias("w"),
)
.flag(
Flag::new("all", FlagType::Bool)
.alias("a"),
)
.flag(
Flag::new("cursor", FlagType::String)
.alias("c"),
)
)
.command(
Command::new("profile")
.description("pro <handle>")
.alias("pro")
.action(profile)
)
.command(
Command::new("openai")
.description("openai <text>")
.alias("chat")
.action(openai)
)
.command(
Command::new("openai-key")
.description("openai-key <API_KEY>")
.action(openai_key),
)
;
app.run(args);
}
fn ascii_art(c: &Context) {
if let Ok(t) = c.string_flag("type") {
c_ascii(&t);
} else {
c_ascii("color");
}
}
fn bot(c: &Context) {
loop {
c_bot(c);
}
}
fn follow_all(_c: &Context) {
c_follow_all();
}
fn openai_key(c: &Context) {
c_openai_key(c);
}
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 refresh(_c: &Context) {
let server = data_toml(&"host");
let h = async {
let session = session::get_request().await;
if session == "err" {
let res = refresh::post_request().await;
println!("{}", res);
w_cfg(&server, &res)
} else {
println!("no refresh");
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn notify(c: &Context) {
refresh(c);
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 did(c: &Context) {
refresh(c);
let h = async {
if c.args.len() == 0 {
let j = describe::get_request(data_toml(&"handle")).await;
println!("{}", j);
} else {
let j = describe::get_request(c.args[0].to_string()).await;
println!("{}", j);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn timeline(c: &Context) {
refresh(c);
let h = async {
if c.args.len() == 0 {
let str = timeline_author::get_request(data_toml(&"handle").to_string());
println!("{}",str.await);
} else {
let str = timeline_author::get_request(c.args[0].to_string());
println!("{}",str.await);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn post(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let h = async {
if let Ok(link) = c.string_flag("link") {
let e = link.chars().count();
let s = 0;
let str = post_link::post_request(m.to_string(), link.to_string(), s, e.try_into().unwrap());
println!("{}",str.await);
} else {
let str = post::post_request(m.to_string());
println!("{}",str.await);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn like(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let h = async {
if let Ok(uri) = c.string_flag("uri") {
let str = like::post_request(m.to_string(), uri);
println!("{}",str.await);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn repost(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let h = async {
if let Ok(uri) = c.string_flag("uri") {
let str = repost::post_request(m.to_string(), uri);
println!("{}",str.await);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn follow(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let h = async {
let handle = data_toml(&"handle");
if let Ok(cursor) = c.string_flag("cursor") {
let str = follow::post_request(m.to_string());
println!("{}", str.await);
//let str = follow::delete_request(m.to_string(), delete.to_string());
//let str = followers::get_request(handle,Some("".to_string()));
//let str = followers::get_request(handle,Some(cursor.to_string()));
//let str = follows::get_request(handle,Some("".to_string()));
let str = follows::get_request(handle,Some(cursor.to_string()));
println!("{}", str.await);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn profile(c: &Context) {
refresh(c);
let h = async {
if c.args.len() == 0 {
let j = profile::get_request(data_toml(&"handle")).await;
println!("{}", j);
} else {
let j = profile::get_request(c.args[0].to_string()).await;
println!("{}", j);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn mention(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let h = async {
let str = profile::get_request(m.to_string()).await;
let profile: ProfileIdentityResolve = serde_json::from_str(&str).unwrap();
let udid = profile.did;
let handle = m.to_string();
let at = "@".to_owned() + &handle;
let e = at.chars().count();
let s = 0;
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);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn reply(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let h = async {
if let Ok(cid) = c.string_flag("cid") {
if let Ok(uri) = c.string_flag("uri") {
if let Ok(link) = c.string_flag("link") {
let s = 0;
let e = link.chars().count();
let str = reply_link::post_request(m.to_string(), link.to_string(), s, e.try_into().unwrap(), cid.to_string(), uri.to_string(), cid.to_string(), uri.to_string()).await;
println!("{}", str);
} else {
let str = reply::post_request(m.to_string(), cid.to_string(), uri.to_string(), cid.to_string(), uri.to_string()).await;
println!("{}", str);
}
}
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn reply_og(c: &Context) {
refresh(c);
let m = c.args[0].to_string();
let link = c.string_flag("link").unwrap();
let cid = c.string_flag("cid").unwrap();
let uri = c.string_flag("uri").unwrap();
let title = c.string_flag("title").unwrap();
let desc = c.string_flag("description").unwrap();
let img = c.string_flag("img").unwrap();
let h = async {
let str = reply_og::post_request(m, link, cid, uri, img, title, desc);
println!("{}",str.await);
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}
fn openai(c: &Context) {
let m = c.args[0].to_string();
let h = async {
let str = openai::post_request(m.to_string()).await;
println!("{}", str);
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res
}

57
src/mention.rs Normal file

@ -0,0 +1,57 @@
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, at: String, udid: String, s: i32, e: i32) -> 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!({
"did": did.to_string(),
"repo": handle.to_string(),
"collection": col.to_string(),
"record": {
"text": at.to_string() + &" ".to_string() + &text.to_string(),
"$type": "app.bsky.feed.post",
"createdAt": d.to_string(),
"facets": [
{
"$type": "app.bsky.richtext.facet",
"index": {
"byteEnd": e,
"byteStart": s
},"features": [
{
"did": udid.to_string(),
"$type": "app.bsky.richtext.facet#mention"
}
]
}
]
},
}));
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
}

31
src/notify.rs Normal 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

@ -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
}

60
src/openai.rs Normal file

@ -0,0 +1,60 @@
extern crate reqwest;
use crate::data::Open;
use serde_json::{json};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
struct OpenChar {
choices: Vec<ChoicesChar>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ChoicesChar {
message: OpenContent,
}
#[derive(Serialize, Deserialize, Debug)]
struct OpenContent {
content: String,
}
pub async fn post_request(prompt: String) -> String {
let data = Open::new().unwrap();
let data = Open {
api: data.api,
};
let setting = "あなたはyuiという作品のアイと呼ばれるキャラクターです。第一人称は「アイ」です。
調
調
123
";
let post = Some(json!({
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": &setting.to_string()},
{"role": "user", "content": &prompt.to_string()},
]
}));
let client = reqwest::Client::new();
let res = client
.post("https://api.openai.com/v1/chat/completions")
.header("Authorization", "Bearer ".to_owned() + &data.api)
.json(&post)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
let p: OpenChar = serde_json::from_str(&res).unwrap();
let o = &p.choices[0].message.content;
return o.to_string()
}

42
src/post.rs Normal file

@ -0,0 +1,42 @@
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) -> 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": text.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
}

56
src/post_link.rs Normal file

@ -0,0 +1,56 @@
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) -> 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(),
"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
}

22
src/profile.rs Normal file

@ -0,0 +1,22 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
pub async fn get_request(user: String) -> String {
let token = data_toml(&"access");
let url = url(&"profile_get") + &"?handle=" + &user;
let client = reqwest::Client::new();
let res = client
.get(url)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

21
src/refresh.rs Normal 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

@ -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_root: String, uri_root: 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_root.to_string(),
"uri": uri_root.to_string()
},
"parent": {
"cid": cid.to_string(),
"uri": uri.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

@ -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_root: String, uri_root: 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_root.to_string(),
"uri": uri_root.to_string()
},
"parent": {
"cid": cid.to_string(),
"uri": uri.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
}

68
src/reply_og.rs Normal file

@ -0,0 +1,68 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(m: String, link: String, cid: String, uri: String, img: String, title: String, description: 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": {
"createdAt": d.to_string(),
"text": m.to_string(),
"embed": {
"$type": "app.bsky.embed.external",
"external": {
"uri": link.to_string(),
"thumb": {
"$type": "blob",
"ref": {
"$link": img.to_string()
},
"mimeType": "image/jpeg",
"size": 0
},
"title": title.to_string(),
"description": description.to_string()
}
},
"reply": {
"root": {
"cid": cid.to_string(),
"uri": uri.to_string()
},
"parent": {
"cid": cid.to_string(),
"uri": uri.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
}

45
src/repost.rs Normal file

@ -0,0 +1,45 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn post_request(cid: String, uri: 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.repost".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": {
"subject": {
"uri": uri.to_string(),
"cid": cid.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
}

29
src/session.rs Normal file

@ -0,0 +1,29 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
pub async fn get_request() -> String {
let token = data_toml(&"access");
let url = url(&"session_get");
let client = reqwest::Client::new();
let res = client
.get(url)
.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/timeline_author.rs Normal file

@ -0,0 +1,28 @@
extern crate reqwest;
use crate::data_toml;
use crate::url;
pub async fn get_request(actor: String) -> String {
let token = data_toml(&"access");
let url = url(&"record_list");
let actor = actor.to_string();
//let cursor = cursor.unwrap();
let col = "app.bsky.feed.post".to_string();
let client = reqwest::Client::new();
let res = client
.get(url)
.query(&[("repo", actor),("collection", col)])
//.query(&[("actor", actor),("cursor", cursor)])
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res
}

24
src/token.rs Normal 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
}