Compare commits
5 Commits
main
...
7a595a65ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
7a595a65ca
|
|||
|
73d860bf01
|
|||
|
ce126faf4d
|
|||
|
454de01881
|
|||
|
57365a41a5
|
1
.config/ai/scpt
Submodule
1
.config/ai/scpt
Submodule
Submodule .config/ai/scpt added at e55225eb57
0
.config/keep
Normal file
0
.config/keep
Normal file
@@ -1,5 +0,0 @@
|
|||||||
# Admin handle (bot will respond to admin commands from this user)
|
|
||||||
ADMIN=syui.ai
|
|
||||||
|
|
||||||
# Bluesky host (optional, default: bsky.social)
|
|
||||||
HOST=syu.is
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,5 +17,3 @@ pnpm-lock.yaml
|
|||||||
**Cargo.lock
|
**Cargo.lock
|
||||||
*/target/
|
*/target/
|
||||||
*/**/*.rs.bk
|
*/**/*.rs.bk
|
||||||
.claude
|
|
||||||
.config
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "aibot"
|
name = "ai"
|
||||||
authors = ["syui"]
|
authors = ["syui"]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
FROM syui/aios
|
FROM syui/aios
|
||||||
|
ADD .ssh /root/.ssh
|
||||||
COPY ./target/release/aibot /usr/sbin/aibot
|
|
||||||
|
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
ADD ./test/entrypoint.sh .
|
||||||
|
RUN chmod +x /root/entrypoint.sh
|
||||||
|
RUN pacman -Syu bc --noconfirm
|
||||||
|
|
||||||
|
ENTRYPOINT ["/root/entrypoint.sh"]
|
||||||
|
|||||||
30
README.md
30
README.md
@@ -2,63 +2,63 @@
|
|||||||
|
|
||||||
<img src="./icon/avatar.png" width="100">
|
<img src="./icon/avatar.png" width="100">
|
||||||
|
|
||||||
- name : aibot
|
- name : ai bot
|
||||||
- base : [aios](https://git.syui.ai/ai/os)
|
- base : [ai os](https://git.syui.ai/ai/os)
|
||||||
- host : [yui.syui.ai](https://bsky.app/profile/yui.syui.ai), [ai.syu.is](https://syu.is/profile/did:plc:6qyecktefllvenje24fcxnie)
|
- host : [yui.syui.ai](https://bsky.app/profile/yui.syui.ai), [ai.syu.is](https://web.syu.is/profile/ai.syu.is)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ aibot
|
$ ai
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ docker run -it syui/aios aibot
|
$ docker run -it syui/aios ai
|
||||||
```
|
```
|
||||||
|
|
||||||
### build
|
### build
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ cargo build
|
$ cargo build
|
||||||
$ ./target/debug/aibot ai
|
$ ./target/debug/ai ai
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ aibot ai -t avatar
|
$ ai ai -t avatar
|
||||||
```
|
```
|
||||||
|
|
||||||
### login
|
### login
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# aibot login $handle -p $password
|
# ai login $handle -p $password
|
||||||
$ aibot l yui.syui.ai -p password
|
$ ai l yui.syui.ai -p password
|
||||||
|
|
||||||
$ cat ~/.config/ai/token.toml
|
$ cat ~/.config/ai/token.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# aibot l $handle -p $password -s $server
|
# ai l $handle -p $password -s $server
|
||||||
$ aibot l ai.syu.is -p password -s syu.is
|
$ ai l ai.syu.is -p password -s syu.is
|
||||||
```
|
```
|
||||||
|
|
||||||
### refresh
|
### refresh
|
||||||
|
|
||||||
```
|
```
|
||||||
$ aibot r
|
$ ai r
|
||||||
```
|
```
|
||||||
|
|
||||||
### notify
|
### notify
|
||||||
|
|
||||||
```
|
```
|
||||||
$ aibot n
|
$ ai n
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
$ aibot n | jq .
|
$ ai n | jq .
|
||||||
```
|
```
|
||||||
|
|
||||||
### bot
|
### bot
|
||||||
|
|
||||||
```
|
```
|
||||||
$ aibot bot
|
$ ai bot
|
||||||
```
|
```
|
||||||
|
|
||||||
|command|sub|type|link|auth|
|
|command|sub|type|link|auth|
|
||||||
|
|||||||
19
compose.yml
19
compose.yml
@@ -1,16 +1,11 @@
|
|||||||
services:
|
services:
|
||||||
bot:
|
aios:
|
||||||
|
#image: syui/aios
|
||||||
|
#command: ai bot -a syui.syu.is
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
restart: always
|
||||||
image: syui/aios:custom
|
env_file:
|
||||||
container_name: aibot
|
- .env
|
||||||
restart: unless-stopped
|
|
||||||
command: ["aibot", "bot", "-a", "${ADMIN:-syui.ai}"]
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./.config/syui/ai/bot:/root/.config/syui/ai/bot
|
- ./.config:/root/.config
|
||||||
logging:
|
|
||||||
driver: json-file
|
|
||||||
options:
|
|
||||||
max-size: "10m"
|
|
||||||
max-file: "3"
|
|
||||||
|
|||||||
122
src/bot.rs
122
src/bot.rs
@@ -167,9 +167,7 @@ pub fn c_bot(c: &Context) {
|
|||||||
|| com.contains("うらない") == true
|
|| com.contains("うらない") == true
|
||||||
|| com.contains("うらなって") == true
|
|| com.contains("うらなって") == true
|
||||||
{
|
{
|
||||||
let script_path = data_scpt(&"ai");
|
let _output = Command::new(data_scpt(&"ai"))
|
||||||
println!("[fortune] script: {}", script_path);
|
|
||||||
let output = Command::new(&script_path)
|
|
||||||
.arg(&"atproto").arg(&"fortune")
|
.arg(&"atproto").arg(&"fortune")
|
||||||
.arg(&handle)
|
.arg(&handle)
|
||||||
.arg(&did)
|
.arg(&did)
|
||||||
@@ -180,25 +178,11 @@ pub fn c_bot(c: &Context) {
|
|||||||
.arg(&host)
|
.arg(&host)
|
||||||
.arg(&prompt)
|
.arg(&prompt)
|
||||||
.arg(&prompt_sub)
|
.arg(&prompt_sub)
|
||||||
.output();
|
.output()
|
||||||
match output {
|
.expect("zsh");
|
||||||
Ok(out) => {
|
|
||||||
let stdout = String::from_utf8_lossy(&out.stdout);
|
|
||||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
||||||
println!("[fortune] stdout: {}", stdout);
|
|
||||||
if !stderr.is_empty() {
|
|
||||||
println!("[fortune] stderr: {}", stderr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("[fortune] error: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||||
} else if com == "card" || com == "/card" {
|
} else if com == "card" || com == "/card" {
|
||||||
let script_path = data_scpt(&"ai");
|
let output = Command::new(data_scpt(&"ai"))
|
||||||
println!("[card] script: {}", script_path);
|
|
||||||
let output = Command::new(&script_path)
|
|
||||||
.arg(&"atproto").arg(&"card")
|
.arg(&"atproto").arg(&"card")
|
||||||
.arg(&handle)
|
.arg(&handle)
|
||||||
.arg(&did)
|
.arg(&did)
|
||||||
@@ -209,26 +193,14 @@ pub fn c_bot(c: &Context) {
|
|||||||
.arg(&host)
|
.arg(&host)
|
||||||
.arg(&prompt)
|
.arg(&prompt)
|
||||||
.arg(&prompt_sub)
|
.arg(&prompt_sub)
|
||||||
.output();
|
.output()
|
||||||
|
.expect("zsh");
|
||||||
match output {
|
let d = String::from_utf8_lossy(&output.stdout);
|
||||||
Ok(out) => {
|
|
||||||
let d = String::from_utf8_lossy(&out.stdout);
|
|
||||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
||||||
println!("[card] stdout: {}", d);
|
|
||||||
if !stderr.is_empty() {
|
|
||||||
println!("[card] stderr: {}", stderr);
|
|
||||||
}
|
|
||||||
let dd = "\n".to_owned() + &d.to_string();
|
let dd = "\n".to_owned() + &d.to_string();
|
||||||
let text_limit = c_char(dd);
|
let text_limit = c_char(dd);
|
||||||
if text_limit.len() > 3 {
|
if text_limit.len() > 3 {
|
||||||
let lines: Vec<&str> = d.lines().collect();
|
//handlev = handle.replace(".", "-").to_string();
|
||||||
handlev = lines.get(0).unwrap_or(&"").trim().to_string();
|
handlev = d.lines().collect::<Vec<_>>()[0].to_string();
|
||||||
// 空白を含む場合や空の場合はhandleから生成
|
|
||||||
if handlev.is_empty() || handlev.contains(' ') {
|
|
||||||
handlev = handle.split('.').next().unwrap_or("").to_string();
|
|
||||||
}
|
|
||||||
if !handlev.is_empty() {
|
|
||||||
link = "https://card.syui.ai/".to_owned() + &handlev;
|
link = "https://card.syui.ai/".to_owned() + &handlev;
|
||||||
e = link.chars().count();
|
e = link.chars().count();
|
||||||
let str_rep = reply_link::post_request(
|
let str_rep = reply_link::post_request(
|
||||||
@@ -245,13 +217,6 @@ pub fn c_bot(c: &Context) {
|
|||||||
println!("{}", str_rep);
|
println!("{}", str_rep);
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("[card] script error: {}", e);
|
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if com == "fav" || com == "/fav" {
|
} else if com == "fav" || com == "/fav" {
|
||||||
let output = Command::new(data_scpt(&"ai"))
|
let output = Command::new(data_scpt(&"ai"))
|
||||||
.arg(&"atproto").arg(&"fav")
|
.arg(&"atproto").arg(&"fav")
|
||||||
@@ -466,42 +431,6 @@ pub fn c_bot(c: &Context) {
|
|||||||
println!("{}", str_rep);
|
println!("{}", str_rep);
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||||
}
|
}
|
||||||
} else if com == "game" || com == "/game" {
|
|
||||||
let output = Command::new(data_scpt(&"ai"))
|
|
||||||
.arg(&"atproto").arg(&"game")
|
|
||||||
.arg(&handle)
|
|
||||||
.arg(&did)
|
|
||||||
.arg(&cid)
|
|
||||||
.arg(&uri)
|
|
||||||
.arg(&cid_root)
|
|
||||||
.arg(&uri_root)
|
|
||||||
.arg(&host)
|
|
||||||
.arg(&prompt)
|
|
||||||
.arg(&prompt_sub)
|
|
||||||
.output()
|
|
||||||
.expect("zsh");
|
|
||||||
let d = String::from_utf8_lossy(&output.stdout);
|
|
||||||
let dd = "\n".to_owned() + &d.to_string();
|
|
||||||
let text_limit = c_char(dd);
|
|
||||||
handlev = d.lines().collect::<Vec<_>>()[0].to_string();
|
|
||||||
link = "https://card.syui.ai/".to_owned() + &handlev;
|
|
||||||
println!("{}", e);
|
|
||||||
e = link.chars().count();
|
|
||||||
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;
|
|
||||||
println!("{}", str_rep);
|
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
|
||||||
}
|
|
||||||
} else if com == "quiz" || com == "/quiz" {
|
} else if com == "quiz" || com == "/quiz" {
|
||||||
println!("admin:{}", admin);
|
println!("admin:{}", admin);
|
||||||
let output = Command::new(data_scpt(&"ai"))
|
let output = Command::new(data_scpt(&"ai"))
|
||||||
@@ -849,9 +778,7 @@ pub fn c_bot_feed(c: &Context) {
|
|||||||
}
|
}
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||||
} else if com == "card" || com == "/card" {
|
} else if com == "card" || com == "/card" {
|
||||||
let script_path = data_scpt(&"ai");
|
let output = Command::new(data_scpt(&"ai"))
|
||||||
println!("[card] script: {}", script_path);
|
|
||||||
let output = Command::new(&script_path)
|
|
||||||
.arg(&"atproto").arg(&"card")
|
.arg(&"atproto").arg(&"card")
|
||||||
.arg(&handle)
|
.arg(&handle)
|
||||||
.arg(&did)
|
.arg(&did)
|
||||||
@@ -862,26 +789,14 @@ pub fn c_bot_feed(c: &Context) {
|
|||||||
.arg(&host)
|
.arg(&host)
|
||||||
.arg(&prompt)
|
.arg(&prompt)
|
||||||
.arg(&prompt_sub)
|
.arg(&prompt_sub)
|
||||||
.output();
|
.output()
|
||||||
|
.expect("zsh");
|
||||||
match output {
|
let d = String::from_utf8_lossy(&output.stdout);
|
||||||
Ok(out) => {
|
|
||||||
let d = String::from_utf8_lossy(&out.stdout);
|
|
||||||
let stderr = String::from_utf8_lossy(&out.stderr);
|
|
||||||
println!("[card] stdout: {}", d);
|
|
||||||
if !stderr.is_empty() {
|
|
||||||
println!("[card] stderr: {}", stderr);
|
|
||||||
}
|
|
||||||
let dd = "\n".to_owned() + &d.to_string();
|
let dd = "\n".to_owned() + &d.to_string();
|
||||||
let text_limit = c_char(dd);
|
let text_limit = c_char(dd);
|
||||||
if text_limit.len() > 3 {
|
if text_limit.len() > 3 {
|
||||||
let lines: Vec<&str> = d.lines().collect();
|
//handlev = handle.replace(".", "-").to_string();
|
||||||
handlev = lines.get(0).unwrap_or(&"").trim().to_string();
|
handlev = d.lines().collect::<Vec<_>>()[0].to_string();
|
||||||
// 空白を含む場合や空の場合はhandleから生成
|
|
||||||
if handlev.is_empty() || handlev.contains(' ') {
|
|
||||||
handlev = handle.split('.').next().unwrap_or("").to_string();
|
|
||||||
}
|
|
||||||
if !handlev.is_empty() {
|
|
||||||
link = "https://card.syui.ai/".to_owned() + &handlev;
|
link = "https://card.syui.ai/".to_owned() + &handlev;
|
||||||
e = link.chars().count();
|
e = link.chars().count();
|
||||||
let str_rep = reply_link::post_request(
|
let str_rep = reply_link::post_request(
|
||||||
@@ -898,13 +813,6 @@ pub fn c_bot_feed(c: &Context) {
|
|||||||
println!("{}", str_rep);
|
println!("{}", str_rep);
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
w_cid(cid.to_string(), log_file(&"n1"), true);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("[card] script error: {}", e);
|
|
||||||
w_cid(cid.to_string(), log_file(&"n1"), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if com == "fav" || com == "/fav" {
|
} else if com == "fav" || com == "/fav" {
|
||||||
let output = Command::new(data_scpt(&"ai"))
|
let output = Command::new(data_scpt(&"ai"))
|
||||||
.arg(&"atproto").arg(&"fav")
|
.arg(&"atproto").arg(&"fav")
|
||||||
|
|||||||
12
src/data.rs
12
src/data.rs
@@ -8,7 +8,7 @@ use std::io::Write;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn data_file(s: &str) -> String {
|
pub fn data_file(s: &str) -> String {
|
||||||
let file = "/.config/syui/ai/bot/";
|
let file = "/.config/ai/";
|
||||||
let mut f = shellexpand::tilde("~").to_string();
|
let mut f = shellexpand::tilde("~").to_string();
|
||||||
f.push_str(&file);
|
f.push_str(&file);
|
||||||
let path = Path::new(&f);
|
let path = Path::new(&f);
|
||||||
@@ -24,7 +24,7 @@ pub fn data_file(s: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn log_file(s: &str) -> String {
|
pub fn log_file(s: &str) -> String {
|
||||||
let file = "/.config/syui/ai/bot/txt/";
|
let file = "/.config/ai/txt/";
|
||||||
let mut f = shellexpand::tilde("~").to_string();
|
let mut f = shellexpand::tilde("~").to_string();
|
||||||
f.push_str(&file);
|
f.push_str(&file);
|
||||||
let path = Path::new(&f);
|
let path = Path::new(&f);
|
||||||
@@ -263,7 +263,7 @@ pub fn data_refresh(s: &str) -> String {
|
|||||||
|
|
||||||
pub fn data_scpt(s: &str) -> String {
|
pub fn data_scpt(s: &str) -> String {
|
||||||
let s = String::from(s);
|
let s = String::from(s);
|
||||||
let file = "/.config/syui/ai/bot/scpt/".to_owned() + &s + &".zsh";
|
let file = "/.config/ai/scpt/".to_owned() + &s + &".zsh";
|
||||||
let mut f = shellexpand::tilde("~").to_string();
|
let mut f = shellexpand::tilde("~").to_string();
|
||||||
f.push_str(&file);
|
f.push_str(&file);
|
||||||
return f;
|
return f;
|
||||||
@@ -603,7 +603,7 @@ pub fn w_cid(cid: String, file: String, t: bool) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn c_follow_all() {
|
pub fn c_follow_all() {
|
||||||
let file = "/.config/syui/ai/bot/scpt/follow_all.zsh";
|
let file = "/.config/ai/scpt/follow_all.zsh";
|
||||||
let mut f = shellexpand::tilde("~").to_string();
|
let mut f = shellexpand::tilde("~").to_string();
|
||||||
f.push_str(&file);
|
f.push_str(&file);
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -617,7 +617,7 @@ 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/syui/ai/bot/openai.toml";
|
let l = shellexpand::tilde("~") + "/.config/ai/openai.toml";
|
||||||
let l = l.to_string();
|
let l = l.to_string();
|
||||||
let mut l = fs::File::create(l).unwrap();
|
let mut l = fs::File::create(l).unwrap();
|
||||||
if o != "" {
|
if o != "" {
|
||||||
@@ -628,7 +628,7 @@ 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/syui/ai/bot/openai.toml";
|
let d = shellexpand::tilde("~") + "/.config/ai/openai.toml";
|
||||||
let s = Config::builder()
|
let s = Config::builder()
|
||||||
.add_source(File::with_name(&d))
|
.add_source(File::with_name(&d))
|
||||||
.add_source(config::Environment::with_prefix("APP"))
|
.add_source(config::Environment::with_prefix("APP"))
|
||||||
|
|||||||
@@ -480,8 +480,6 @@ fn bot(c: &Context) {
|
|||||||
loop {
|
loop {
|
||||||
c_bot(c);
|
c_bot(c);
|
||||||
c_bot_feed(c);
|
c_bot_feed(c);
|
||||||
// 10秒待機してCPU使用率を抑制
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +487,6 @@ fn feed_watch(c: &Context) {
|
|||||||
refresh(c);
|
refresh(c);
|
||||||
loop {
|
loop {
|
||||||
c_feed_watch(c);
|
c_feed_watch(c);
|
||||||
std::thread::sleep(std::time::Duration::from_secs(10));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
3
test/entrypoint.sh
Normal file
3
test/entrypoint.sh
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/zsh
|
||||||
|
|
||||||
|
ai l $HANDLE -p $PASSWORD -s $HOST && ai bot -a $ADMIN
|
||||||
Reference in New Issue
Block a user