Compare commits
3 Commits
claude-cod
...
claude
Author | SHA1 | Date | |
---|---|---|---|
a4f7f867f5
|
|||
81db8cfe29
|
|||
6513d626de
|
@ -15,7 +15,8 @@
|
|||||||
"Bash(mkdir:*)",
|
"Bash(mkdir:*)",
|
||||||
"Bash(chmod:*)",
|
"Bash(chmod:*)",
|
||||||
"Bash(git checkout:*)",
|
"Bash(git checkout:*)",
|
||||||
"Bash(git add:*)"
|
"Bash(git add:*)",
|
||||||
|
"Bash(rg:*)"
|
||||||
],
|
],
|
||||||
"deny": []
|
"deny": []
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ path = "src/alias.rs"
|
|||||||
seahorse = "*"
|
seahorse = "*"
|
||||||
reqwest = { version = "*", features = ["blocking", "json"] }
|
reqwest = { version = "*", features = ["blocking", "json"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
shellexpand = "*"
|
|
||||||
config = "*"
|
config = "*"
|
||||||
serde = "*"
|
serde = "*"
|
||||||
serde_json = "*"
|
serde_json = "*"
|
||||||
|
245
src/data.rs
245
src/data.rs
@ -5,94 +5,70 @@ use std::fs;
|
|||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::PathBuf;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
pub fn data_file(s: &str) -> String {
|
/// 設定ディレクトリのベースパス
|
||||||
// 新しい設定ディレクトリ(優先)
|
const CONFIG_BASE_DIR: &str = "~/.config/syui/ai/bot";
|
||||||
let new_config_dir = "/.config/syui/ai/bot/";
|
|
||||||
let mut new_path = shellexpand::tilde("~").to_string();
|
|
||||||
new_path.push_str(&new_config_dir);
|
|
||||||
|
|
||||||
// 旧設定ディレクトリ(互換性のため)
|
/// ホームディレクトリパスを展開するユーティリティ関数
|
||||||
let old_config_dir = "/.config/ai/";
|
/// "~"で始まるパスをユーザーのホームディレクトリに展開します
|
||||||
let mut old_path = shellexpand::tilde("~").to_string();
|
fn expand_home_path(path: &str) -> PathBuf {
|
||||||
old_path.push_str(&old_config_dir);
|
if path.starts_with("~") {
|
||||||
|
let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
|
||||||
// 新しいディレクトリを作成
|
let path_without_tilde = path.strip_prefix("~/").unwrap_or(&path[1..]);
|
||||||
let new_dir = Path::new(&new_path);
|
PathBuf::from(home).join(path_without_tilde)
|
||||||
if !new_dir.is_dir() {
|
} else {
|
||||||
let _ = fs::create_dir_all(new_path.clone());
|
PathBuf::from(path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = match &*s {
|
/// 設定ディレクトリのベースパスを取得し、必要に応じて作成する
|
||||||
"toml" => "token.toml",
|
fn get_config_base_path() -> PathBuf {
|
||||||
"json" => "token.json",
|
let path = expand_home_path(CONFIG_BASE_DIR);
|
||||||
"refresh" => "refresh.toml",
|
if !path.is_dir() {
|
||||||
_ => &format!(".{}", s),
|
let _ = fs::create_dir_all(&path);
|
||||||
|
}
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// サブディレクトリを含む設定パスを取得し、必要に応じて作成する
|
||||||
|
fn get_config_path(subdir: &str) -> PathBuf {
|
||||||
|
let base_path = get_config_base_path();
|
||||||
|
let path = if subdir.is_empty() {
|
||||||
|
base_path
|
||||||
|
} else {
|
||||||
|
base_path.join(subdir)
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_file = new_path.clone() + filename;
|
if !path.is_dir() {
|
||||||
let old_file = old_path + filename;
|
let _ = fs::create_dir_all(&path);
|
||||||
|
}
|
||||||
// 新しいパスにファイルが存在する場合は新しいパスを使用
|
path
|
||||||
if Path::new(&new_file).exists() {
|
|
||||||
return new_file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 旧パスにファイルが存在し、新しいパスに存在しない場合は移行を試行
|
pub fn data_file(s: &str) -> String {
|
||||||
if Path::new(&old_file).exists() && !Path::new(&new_file).exists() {
|
let path = get_config_base_path();
|
||||||
if let Ok(_) = fs::copy(&old_file, &new_file) {
|
let path_str = path.to_string_lossy();
|
||||||
eprintln!("Migrated config file: {} -> {}", old_file, new_file);
|
|
||||||
return new_file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// デフォルトは新しいパス
|
match s {
|
||||||
new_file
|
"toml" => format!("{}/token.toml", path_str),
|
||||||
|
"json" => format!("{}/token.json", path_str),
|
||||||
|
"refresh" => format!("{}/refresh.toml", path_str),
|
||||||
|
_ => format!("{}/.{}", path_str, s),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_file(s: &str) -> String {
|
pub fn log_file(s: &str) -> String {
|
||||||
// 新しい設定ディレクトリ(優先)
|
let path = get_config_path("txt");
|
||||||
let new_log_dir = "/.config/syui/ai/bot/txt/";
|
let path_str = path.to_string_lossy();
|
||||||
let mut new_path = shellexpand::tilde("~").to_string();
|
|
||||||
new_path.push_str(&new_log_dir);
|
|
||||||
|
|
||||||
// 旧設定ディレクトリ(互換性のため)
|
match s {
|
||||||
let old_log_dir = "/.config/ai/txt/";
|
"n1" => format!("{}/notify_cid.txt", path_str),
|
||||||
let mut old_path = shellexpand::tilde("~").to_string();
|
"n2" => format!("{}/notify_cid_run.txt", path_str),
|
||||||
old_path.push_str(&old_log_dir);
|
"c1" => format!("{}/comment_cid.txt", path_str),
|
||||||
|
_ => format!("{}/{}", path_str, s),
|
||||||
// 新しいディレクトリを作成
|
|
||||||
let new_dir = Path::new(&new_path);
|
|
||||||
if !new_dir.is_dir() {
|
|
||||||
let _ = fs::create_dir_all(new_path.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = match &*s {
|
|
||||||
"n1" => "notify_cid.txt",
|
|
||||||
"n2" => "notify_cid_run.txt",
|
|
||||||
"c1" => "comment_cid.txt",
|
|
||||||
_ => s,
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_file = new_path.clone() + filename;
|
|
||||||
let old_file = old_path + filename;
|
|
||||||
|
|
||||||
// 新しいパスにファイルが存在する場合は新しいパスを使用
|
|
||||||
if Path::new(&new_file).exists() {
|
|
||||||
return new_file;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 旧パスにファイルが存在し、新しいパスに存在しない場合は移行を試行
|
|
||||||
if Path::new(&old_file).exists() && !Path::new(&new_file).exists() {
|
|
||||||
if let Ok(_) = fs::copy(&old_file, &new_file) {
|
|
||||||
eprintln!("Migrated log file: {} -> {}", old_file, new_file);
|
|
||||||
return new_file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// デフォルトは新しいパス
|
|
||||||
new_file
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
@ -185,15 +161,14 @@ pub struct BaseUrl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn url(s: &str) -> String {
|
pub fn url(s: &str) -> String {
|
||||||
let s = String::from(s);
|
let data = match Data::new() {
|
||||||
let data = Data::new().unwrap();
|
Ok(data) => data,
|
||||||
let data = Data {
|
Err(_) => {
|
||||||
host: data.host,
|
eprintln!("Error: Configuration file not found at {}/token.toml",
|
||||||
password: data.password,
|
get_config_base_path().display());
|
||||||
handle: data.handle,
|
eprintln!("Please run 'aibot login <handle> -p <password>' first to authenticate.");
|
||||||
did: data.did,
|
std::process::exit(1);
|
||||||
access: data.access,
|
}
|
||||||
refresh: data.refresh,
|
|
||||||
};
|
};
|
||||||
let t = "https://".to_string() + &data.host.to_string() + &"/xrpc/".to_string();
|
let t = "https://".to_string() + &data.host.to_string() + &"/xrpc/".to_string();
|
||||||
let baseurl = BaseUrl {
|
let baseurl = BaseUrl {
|
||||||
@ -250,29 +225,29 @@ pub fn url(s: &str) -> String {
|
|||||||
"follows" => t.to_string() + &baseurl.follows,
|
"follows" => t.to_string() + &baseurl.follows,
|
||||||
"followers" => t.to_string() + &baseurl.followers,
|
"followers" => t.to_string() + &baseurl.followers,
|
||||||
"feed_get" => t.to_string() + &baseurl.feed_get,
|
"feed_get" => t.to_string() + &baseurl.feed_get,
|
||||||
_ => s,
|
_ => s.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_toml(s: &str) -> String {
|
pub fn data_toml(s: &str) -> String {
|
||||||
let s = String::from(s);
|
let data = match Data::new() {
|
||||||
let data = Data::new().unwrap();
|
Ok(data) => data,
|
||||||
let data = Data {
|
Err(_) => {
|
||||||
host: data.host,
|
eprintln!("Error: Configuration file not found at {}/token.toml",
|
||||||
password: data.password,
|
get_config_base_path().display());
|
||||||
handle: data.handle,
|
eprintln!("Please run 'aibot login <handle> -p <password>' first to authenticate.");
|
||||||
did: data.did,
|
std::process::exit(1);
|
||||||
access: data.access,
|
}
|
||||||
refresh: data.refresh,
|
|
||||||
};
|
};
|
||||||
match &*s {
|
|
||||||
|
match s {
|
||||||
"host" => data.host,
|
"host" => data.host,
|
||||||
"password" => data.password,
|
"password" => data.password,
|
||||||
"handle" => data.handle,
|
"handle" => data.handle,
|
||||||
"did" => data.did,
|
"did" => data.did,
|
||||||
"access" => data.access,
|
"access" => data.access,
|
||||||
"refresh" => data.refresh,
|
"refresh" => data.refresh,
|
||||||
_ => s,
|
_ => s.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,41 +263,41 @@ pub fn c_refresh(access: &str, refresh: &str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_refresh(s: &str) -> String {
|
pub fn data_refresh(s: &str) -> String {
|
||||||
let s = String::from(s);
|
let data = match Data::new() {
|
||||||
|
Ok(data) => data,
|
||||||
let data = Data::new().unwrap();
|
Err(_) => {
|
||||||
let data = Data {
|
eprintln!("Error: Configuration file not found at {}/token.toml",
|
||||||
host: data.host,
|
get_config_base_path().display());
|
||||||
password: data.password,
|
eprintln!("Please run 'aibot login <handle> -p <password>' first to authenticate.");
|
||||||
handle: data.handle,
|
std::process::exit(1);
|
||||||
did: data.did,
|
}
|
||||||
access: data.access,
|
|
||||||
refresh: data.refresh,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut _file = match Refresh::new()
|
let mut _file = match Refresh::new() {
|
||||||
{
|
|
||||||
Err(_why) => c_refresh(&data.access, &data.refresh),
|
Err(_why) => c_refresh(&data.access, &data.refresh),
|
||||||
Ok(_) => println!(""),
|
Ok(_) => println!(""),
|
||||||
};
|
};
|
||||||
let refresh = Refresh::new().unwrap();
|
|
||||||
let refresh = Refresh {
|
let refresh = match Refresh::new() {
|
||||||
access: refresh.access,
|
Ok(refresh) => refresh,
|
||||||
refresh: refresh.refresh,
|
Err(_) => {
|
||||||
|
eprintln!("Error: Refresh token file not found.");
|
||||||
|
eprintln!("Please run 'aibot login <handle> -p <password>' to re-authenticate.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match &*s {
|
|
||||||
|
match s {
|
||||||
"access" => refresh.access,
|
"access" => refresh.access,
|
||||||
"refresh" => refresh.refresh,
|
"refresh" => refresh.refresh,
|
||||||
_ => s,
|
_ => s.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_scpt(s: &str) -> String {
|
pub fn data_scpt(s: &str) -> String {
|
||||||
let s = String::from(s);
|
let mut path = get_config_path("scpt");
|
||||||
let file = "/.config/ai/scpt/".to_owned() + &s + &".zsh";
|
path.push(format!("{}.zsh", s));
|
||||||
let mut f = shellexpand::tilde("~").to_string();
|
path.to_string_lossy().to_string()
|
||||||
f.push_str(&file);
|
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@ -590,7 +565,20 @@ pub fn w_cfg(h: &str, res: &str, password: &str) {
|
|||||||
let mut f = fs::File::create(f.clone()).unwrap();
|
let mut f = fs::File::create(f.clone()).unwrap();
|
||||||
let mut ff = fs::File::create(ff.clone()).unwrap();
|
let mut ff = fs::File::create(ff.clone()).unwrap();
|
||||||
f.write_all(&res.as_bytes()).unwrap();
|
f.write_all(&res.as_bytes()).unwrap();
|
||||||
let json: Token = serde_json::from_str(&res).unwrap();
|
// Check if response contains an error
|
||||||
|
if res.contains("\"error\"") {
|
||||||
|
eprintln!("Authentication error: {}", res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json: Token = match serde_json::from_str(&res) {
|
||||||
|
Ok(token) => token,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("JSON parse error: {}", e);
|
||||||
|
eprintln!("Response: {}", res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let datas = Data {
|
let datas = Data {
|
||||||
host: h.to_string(),
|
host: h.to_string(),
|
||||||
password: password.to_string(),
|
password: password.to_string(),
|
||||||
@ -659,11 +647,10 @@ pub fn w_cid(cid: String, file: String, t: bool) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn c_follow_all() {
|
pub fn c_follow_all() {
|
||||||
let file = "/.config/ai/scpt/follow_all.zsh";
|
let path = expand_home_path("~/.config/syui/ai/bot/scpt/follow_all.zsh");
|
||||||
let mut f = shellexpand::tilde("~").to_string();
|
|
||||||
f.push_str(&file);
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
let output = Command::new(&f).output().expect("zsh");
|
let output = Command::new(path.to_str().unwrap()).output().expect("zsh");
|
||||||
let d = String::from_utf8_lossy(&output.stdout);
|
let d = String::from_utf8_lossy(&output.stdout);
|
||||||
let d = "\n".to_owned() + &d.to_string();
|
let d = "\n".to_owned() + &d.to_string();
|
||||||
println!("{}", d);
|
println!("{}", d);
|
||||||
@ -673,9 +660,10 @@ pub fn c_openai_key(c: &Context) {
|
|||||||
let api = c.args[0].to_string();
|
let api = c.args[0].to_string();
|
||||||
let o = "api='".to_owned() + &api.to_string() + &"'".to_owned();
|
let o = "api='".to_owned() + &api.to_string() + &"'".to_owned();
|
||||||
let o = o.to_string();
|
let o = o.to_string();
|
||||||
let l = shellexpand::tilde("~") + "/.config/ai/openai.toml";
|
|
||||||
let l = l.to_string();
|
let path = expand_home_path("~/.config/syui/ai/bot/openai.toml");
|
||||||
let mut l = fs::File::create(l).unwrap();
|
|
||||||
|
let mut l = fs::File::create(&path).unwrap();
|
||||||
if o != "" {
|
if o != "" {
|
||||||
l.write_all(&o.as_bytes()).unwrap();
|
l.write_all(&o.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
@ -684,9 +672,10 @@ pub fn c_openai_key(c: &Context) {
|
|||||||
|
|
||||||
impl Open {
|
impl Open {
|
||||||
pub fn new() -> Result<Self, ConfigError> {
|
pub fn new() -> Result<Self, ConfigError> {
|
||||||
let d = shellexpand::tilde("~") + "/.config/ai/openai.toml";
|
let path = expand_home_path("~/.config/syui/ai/bot/openai.toml");
|
||||||
|
|
||||||
let s = Config::builder()
|
let s = Config::builder()
|
||||||
.add_source(File::with_name(&d))
|
.add_source(File::with_name(path.to_str().unwrap()))
|
||||||
.add_source(config::Environment::with_prefix("APP"))
|
.add_source(config::Environment::with_prefix("APP"))
|
||||||
.build()?;
|
.build()?;
|
||||||
s.try_deserialize()
|
s.try_deserialize()
|
||||||
|
82
src/main.rs
82
src/main.rs
@ -105,7 +105,7 @@ fn main() {
|
|||||||
.command(
|
.command(
|
||||||
Command::new("login")
|
Command::new("login")
|
||||||
.alias("l")
|
.alias("l")
|
||||||
.description("l <handle> -p <password>\n\t\t\tl <handle> -p <password> -s <server>")
|
.description("l <handle> -p <password>\n\t\t\tl <handle> -p <password> -s <server>\n\t\t\tl <handle> -p <password> -c <2fa_code>")
|
||||||
.action(token)
|
.action(token)
|
||||||
.flag(
|
.flag(
|
||||||
Flag::new("password", FlagType::String)
|
Flag::new("password", FlagType::String)
|
||||||
@ -117,6 +117,11 @@ fn main() {
|
|||||||
.description("server flag")
|
.description("server flag")
|
||||||
.alias("s"),
|
.alias("s"),
|
||||||
)
|
)
|
||||||
|
.flag(
|
||||||
|
Flag::new("code", FlagType::String)
|
||||||
|
.description("2FA authentication code")
|
||||||
|
.alias("c"),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.command(
|
.command(
|
||||||
Command::new("refresh")
|
Command::new("refresh")
|
||||||
@ -503,18 +508,19 @@ fn openai_key(c: &Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn token(c: &Context) {
|
fn token(c: &Context) {
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Handle is required.");
|
||||||
|
eprintln!("Usage: aibot login <handle> -p <password>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
if let Ok(p) = c.string_flag("password") {
|
if let Ok(p) = c.string_flag("password") {
|
||||||
if let Ok(s) = c.string_flag("server") {
|
let server = c.string_flag("server").unwrap_or_else(|_| "bsky.social".to_string());
|
||||||
let res = token::post_request(m.to_string(), p.to_string(), s.to_string()).await;
|
let code = c.string_flag("code").ok();
|
||||||
w_cfg(&s, &res, &p);
|
|
||||||
} else {
|
let res = token::post_request(m.to_string(), p.to_string(), server.to_string(), code).await;
|
||||||
let res =
|
w_cfg(&server, &res, &p);
|
||||||
token::post_request(m.to_string(), p.to_string(), "bsky.social".to_string())
|
|
||||||
.await;
|
|
||||||
w_cfg(&"bsky.social", &res, &p);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
|
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
|
||||||
@ -530,7 +536,7 @@ fn refresh(_c: &Context) {
|
|||||||
let m = data_toml(&"handle");
|
let m = data_toml(&"handle");
|
||||||
let p = data_toml(&"password");
|
let p = data_toml(&"password");
|
||||||
let s = data_toml(&"host");
|
let s = data_toml(&"host");
|
||||||
let res = token::post_request(m.to_string(), p.to_string(), s.to_string()).await;
|
let res = token::post_request(m.to_string(), p.to_string(), s.to_string(), None).await;
|
||||||
w_cfg(&s, &res, &p);
|
w_cfg(&s, &res, &p);
|
||||||
} else {
|
} else {
|
||||||
w_refresh(&res);
|
w_refresh(&res);
|
||||||
@ -599,6 +605,11 @@ fn timeline(c: &Context) {
|
|||||||
|
|
||||||
fn post(c: &Context) {
|
fn post(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Post text is required.");
|
||||||
|
eprintln!("Usage: aibot post <text>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
if let Ok(link) = c.string_flag("link") {
|
if let Ok(link) = c.string_flag("link") {
|
||||||
@ -618,6 +629,11 @@ fn post(c: &Context) {
|
|||||||
|
|
||||||
fn delete(c: &Context) {
|
fn delete(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Record key is required.");
|
||||||
|
eprintln!("Usage: aibot delete <rkey> --col <collection>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
if let Ok(col) = c.string_flag("col") {
|
if let Ok(col) = c.string_flag("col") {
|
||||||
@ -631,6 +647,11 @@ fn delete(c: &Context) {
|
|||||||
|
|
||||||
fn like(c: &Context) {
|
fn like(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: CID is required.");
|
||||||
|
eprintln!("Usage: aibot like <cid> --uri <uri>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
if let Ok(uri) = c.string_flag("uri") {
|
if let Ok(uri) = c.string_flag("uri") {
|
||||||
@ -783,6 +804,11 @@ fn game_login(c: &Context) {
|
|||||||
|
|
||||||
fn repost(c: &Context) {
|
fn repost(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: CID is required.");
|
||||||
|
eprintln!("Usage: aibot repost <cid> --uri <uri>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
if let Ok(uri) = c.string_flag("uri") {
|
if let Ok(uri) = c.string_flag("uri") {
|
||||||
@ -796,6 +822,11 @@ fn repost(c: &Context) {
|
|||||||
|
|
||||||
fn follow(c: &Context) {
|
fn follow(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Handle is required.");
|
||||||
|
eprintln!("Usage: aibot follow <handle>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
let handle = data_toml(&"handle");
|
let handle = data_toml(&"handle");
|
||||||
@ -819,10 +850,10 @@ fn profile(c: &Context) {
|
|||||||
let h = async {
|
let h = async {
|
||||||
if c.args.len() == 0 {
|
if c.args.len() == 0 {
|
||||||
let j = profile::get_request(data_toml(&"handle")).await;
|
let j = profile::get_request(data_toml(&"handle")).await;
|
||||||
println!("{}", j);
|
print!("{}", j);
|
||||||
} else {
|
} else {
|
||||||
let j = profile::get_request(c.args[0].to_string()).await;
|
let j = profile::get_request(c.args[0].to_string()).await;
|
||||||
println!("{}", j);
|
print!("{}", j);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
|
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
|
||||||
@ -831,6 +862,11 @@ fn profile(c: &Context) {
|
|||||||
|
|
||||||
fn mention(c: &Context) {
|
fn mention(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Handle is required.");
|
||||||
|
eprintln!("Usage: aibot mention <handle>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
let str = profile::get_request(m.to_string()).await;
|
let str = profile::get_request(m.to_string()).await;
|
||||||
@ -860,6 +896,11 @@ fn mention(c: &Context) {
|
|||||||
|
|
||||||
fn reply(c: &Context) {
|
fn reply(c: &Context) {
|
||||||
refresh(c);
|
refresh(c);
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Reply text is required.");
|
||||||
|
eprintln!("Usage: aibot reply <text> --cid <cid> --uri <uri>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
if let Ok(cid) = c.string_flag("cid") {
|
if let Ok(cid) = c.string_flag("cid") {
|
||||||
@ -899,6 +940,11 @@ fn reply(c: &Context) {
|
|||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn c_img_upload(c: &Context) -> reqwest::Result<()> {
|
async fn c_img_upload(c: &Context) -> reqwest::Result<()> {
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Image file path is required.");
|
||||||
|
eprintln!("Usage: aibot img_upload <image_file>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let token = data_refresh(&"access");
|
let token = data_refresh(&"access");
|
||||||
let atoken = "Authorization: Bearer ".to_owned() + &token;
|
let atoken = "Authorization: Bearer ".to_owned() + &token;
|
||||||
let con = "Content-Type: image/png";
|
let con = "Content-Type: image/png";
|
||||||
@ -930,6 +976,11 @@ fn img_upload(c: &Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn img_post(c: &Context) {
|
fn img_post(c: &Context) {
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Text is required.");
|
||||||
|
eprintln!("Usage: aibot img_post <text> --link <link> --cid <cid> --uri <uri>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let link = c.string_flag("link").unwrap();
|
let link = c.string_flag("link").unwrap();
|
||||||
let cid = c.string_flag("cid").unwrap();
|
let cid = c.string_flag("cid").unwrap();
|
||||||
@ -976,6 +1027,11 @@ fn reply_og(c: &Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn openai(c: &Context) {
|
fn openai(c: &Context) {
|
||||||
|
if c.args.is_empty() {
|
||||||
|
eprintln!("Error: Message is required.");
|
||||||
|
eprintln!("Usage: aibot openai <message>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
let m = c.args[0].to_string();
|
let m = c.args[0].to_string();
|
||||||
let h = async {
|
let h = async {
|
||||||
let str = openai::post_request(m.to_string()).await;
|
let str = openai::post_request(m.to_string()).await;
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
use crate::http_client::HttpClient;
|
use crate::http_client::HttpClient;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub async fn post_request(handle: String, pass: String, host: String) -> String {
|
pub async fn post_request(handle: String, pass: String, host: String, auth_factor_token: Option<String>) -> String {
|
||||||
let url = format!("https://{}/xrpc/com.atproto.server.createSession", host);
|
let url = format!("https://{}/xrpc/com.atproto.server.createSession", host);
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("identifier", &handle);
|
map.insert("identifier", &handle);
|
||||||
map.insert("password", &pass);
|
map.insert("password", &pass);
|
||||||
|
|
||||||
|
// Add 2FA code if provided
|
||||||
|
if let Some(code) = &auth_factor_token {
|
||||||
|
map.insert("authFactorToken", code);
|
||||||
|
}
|
||||||
|
|
||||||
let client = HttpClient::new();
|
let client = HttpClient::new();
|
||||||
|
|
||||||
match client.post_json(&url, &map).await {
|
match client.post_json(&url, &map).await {
|
||||||
|
Reference in New Issue
Block a user