Compare commits

..

31 Commits

Author SHA1 Message Date
d0746a7af6 update handle
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 11m36s
2025-06-16 20:41:03 +09:00
a4f7f867f5 fix cmd
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 12m45s
2025-06-11 11:49:53 +09:00
81db8cfe29 add 2fa
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2025-06-11 11:40:21 +09:00
6513d626de rm shellexpand
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 11m53s
2025-06-09 01:43:05 +09:00
a17d2c9d66 Major refactoring: HTTP client unification and project restructuring
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 13m53s
## HTTP Client Refactoring
- Create unified HttpClient module (src/http_client.rs)
- Refactor 24 files to use shared HTTP client
- Replace .unwrap() with proper error handling
- Eliminate code duplication in HTTP requests

## Project Restructuring
- Rename package: ai → aibot
- Add dual binary support: aibot (main) + ai (compatibility alias)
- Migrate config directory: ~/.config/ai/ → ~/.config/syui/ai/bot/
- Implement backward compatibility with automatic migration

## Testing Infrastructure
- Add unit tests for HttpClient
- Create test infrastructure with cargo-make
- Add test commands: test, test-quick, test-verbose

## Documentation
- Complete migration guide with step-by-step instructions
- Updated development guide with new structure
- HTTP client API reference documentation
- Comprehensive refactoring summary

## Files Changed
- Modified: 24 source files (HTTP client integration)
- Added: src/http_client.rs, src/alias.rs, src/tests/
- Added: 5 documentation files in docs/
- Added: migration setup script

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-06 23:47:12 +09:00
998777d46a test game
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 29m13s
2025-01-22 17:54:42 +09:00
61d7df6922 fix login
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
2025-01-20 17:31:45 +09:00
eb8f1b17c8 fix bc
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 18s
2025-01-20 17:30:34 +09:00
fc5e942f0c add card
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 6s
2024-12-27 16:40:11 +09:00
d2a394cec2 fix help
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-11-08 08:14:41 +09:00
27d5dac208 update gpt-4o-mini 2024-11-08 08:14:40 +09:00
ddd6f37118 fix check cid 2024-11-08 08:14:40 +09:00
14ca1bcdee check feed watch cid
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-08-18 02:17:58 +09:00
84efc31248 add feed watch
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-08-18 01:30:48 +09:00
3904c576f0 add cargo toml
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2024-08-04 08:10:53 +09:00
08436c0a56 fix docker
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2024-08-04 08:02:53 +09:00
d5603fda52 test planet 2024-08-04 08:02:53 +09:00
4633901ca0 test planet 2024-08-04 08:02:53 +09:00
a8fd189a63 fix bot len return 2024-08-04 08:02:53 +09:00
b540d0c007 fix down feed-generator
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2024-05-23 02:17:31 +09:00
bf31cf2a8f add bot comment system
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
2024-05-14 03:38:59 +09:00
840320d0d2 add bot custom feed
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
2024-04-22 22:30:43 +09:00
5d60645c0f fix admin
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 14s
2024-04-12 08:22:54 +09:00
e7c06cf9d1 test manga
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 4s
2024-04-04 13:53:39 +09:00
025b24b8f0 fix ten post
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-03-31 01:34:23 +09:00
7bbc3370d7 fix fortune
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
2024-03-30 02:57:38 +09:00
d6777a0c6a fix sp 2024-03-30 02:57:38 +09:00
87a333d744 fix card handle 2024-03-30 02:57:38 +09:00
2f0bfe08b0 fix print 2024-03-30 02:57:37 +09:00
97856f3765 add host 2024-03-30 02:57:37 +09:00
7b03adda1f v0.1
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 9s
2024-03-05 04:09:43 +09:00
118 changed files with 4309 additions and 7406 deletions

View File

@@ -0,0 +1,23 @@
{
"permissions": {
"allow": [
"Bash(find:*)",
"Bash(cargo check:*)",
"Bash(cargo test)",
"Bash(cargo test:*)",
"Bash(grep:*)",
"Bash(cargo install:*)",
"Bash(cargo make:*)",
"Bash(cargo:*)",
"Bash(ls:*)",
"Bash(./target/debug/aibot --help)",
"Bash(./target/debug/ai --help)",
"Bash(mkdir:*)",
"Bash(chmod:*)",
"Bash(git checkout:*)",
"Bash(git add:*)",
"Bash(rg:*)"
],
"deny": []
}
}

1
.config/ai/scpt Submodule

Submodule .config/ai/scpt added at a1905d104b

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +0,0 @@
#!/bin/zsh
url=https://api.syui.ai
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
f_reid_user=$HOME/.config/ai/txt/card_reid_user.txt
n=`cat $f_reid_user|wc -l`
for ((i=1;i<=$n;i++))
do
uid=`cat $f_reid_user|awk "NR==$i"`
r=`echo $(($RANDOM % 10))`
if [ $r -eq 1 ];then
card=`echo $(($RANDOM % 15))`
cp=`echo $(($RANDOM % 300 + 50))`
else
card=0
cp=`echo $(($RANDOM % 100 + 1))`
fi
ss=$(($RANDOM % 10))
if [ 13 -ne $card ] && [ $ss -eq 1 ];then
card=13
fi
s=$(($RANDOM % 2))
if [ $s -eq 1 ];then
s=super
plus=$(($RANDOM % 500 + 200))
cp=$((cp + plus))
else
s=normal
fi
if [ $card -eq 13 ];then
plus=$(($RANDOM % 1000 + 300))
cp=$((cp + plus))
fi
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\"}" -s $url/cards`
echo $tmp
card=`echo $tmp|jq -r .card`
cp=`echo $tmp|jq -r .cp`
echo "[card]"
echo "id : ${card}"
echo "cp : ${cp}"
done

View File

@@ -1,215 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ];then
echo no option
echo "/chara start"
exit
fi
chara_qa="あなたはサッカーをしています。
試合中、ふと目にするのは?
1 : 開けた青空
2 : 真っ白な柱
3 : 風になびく芝生
数字を入れて答えてね。
/chara 数字"
chara_qb="ここは研究室。
実験のため道具を手に持っています。
1 : 淡い液体が入ったガラス瓶
2 : 清潔なシーツ
3 : 観葉植物"
chara_qc="宇宙に打ち上げられたロケットから地球を見ます。
何が見えましたか?
1 : 別の宇宙船
2 : 飛行機
3 : 大きな島"
chara_ba="
☑ 平和を願う
☑ 協調性は高いが自己主張は弱い
☑ 相談がうまい
---"
chara_bb="
☑ 変化自在
☑ 世間離れしており常識知らず
☑ 真面目で芯が強い
---"
chara_bc="
☑ 思慮深い
☑ 一人の時間が好きで冷たく見える
☑ 周りを観察している
---"
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"ten_post\": \"$ten_char\", \"ten_kai\":0,\"ten_su\":$first_ten,\"ten\": true,\"token\":\"$token\"}" -s $host/users/$uid`
data_user_card=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"`
ten_data=`curl -sL "$host/users?itemsPerPage=3000"|jq ".|sort_by(.ten_su)|reverse|.[]|select(.ten_su != 0)"`
data_user_card=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"`
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
host_card=https://card.syui.ai/json/card.json
#host_card_json=`curl -sL $host_card`
ran=$(($RANDOM % 3))
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
handle=$1
did=$2
username=`echo $1|cut -d . -f 1`
cid=$3
uri=$4
option=$5
yui_did=did:plc:4hqjfn7m6n5hno3doamuhgef
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
aiten=`echo $data|jq -r .aiten`
ten_post=`echo $data|jq -r .ten_post`
ten_su=`echo $data|jq -r .ten_su`
ten_kai=`echo $data|jq -r .ten_kai`
ten_delete=`echo $data|jq -r .ten_delete`
ten_bool=`echo $data|jq -r .ten`
day_at=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 days ago'`
ten_at_n=`date --iso-8601=seconds`
d=`date +"%Y-%m-%d"`
ten_at=`echo $data|jq -r .ten_at`
ten_at=`date -d "$ten_at" +"%Y-%m-%d"`
data_card=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"`
# uri_post=`echo '{"uri":"at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3k3zr5b336o2u","cid":"bafyreierpw23cxvx4e3cjzd3h6r4crz646zb2ana6cmmsxn4z5rpupl35e"}'|jq -r .uri|cut -d / -f 5`
# https://bsky.app/profile/$did/post/$uri_post
tmp_atr='{"uri":"at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3k3zr5b336o2u","cid":"bafyreierpw23cxvx4e3cjzd3h6r4crz646zb2ana6cmmsxn4z5rpupl35e"}'
function chara_check(){
#card_check=`echo $data_card|jq -r ".[]|select(.card == 48 or .card == 49 or .card == 50 or .card == 51 or .card == 52 or .card == 53)"`
#card_check=`echo $data_card|jq -r ".[]|select(.card == 54 or .card == 55 or .card == 56)"`
card_check=`echo $data_card|jq -r ".[]|select(.card == 58 or .card == 53 or .card == 59)"`
if [ -n "$card_check" ];then
echo you already have chara-card
exit
fi
}
function chara_start() {
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"ten_su\":0, \"ten_kai\":1, \"token\":\"$token\"}" -s $host/users/$uid`
echo "$chara_qa"
exit
}
function chara_post(){
case $1 in
ponta)
card=53
text=$chara_ba
title="[ポンタ]"
desc="緑色"
;;
octo)
card=58
text=$chara_bb
title="[オクトカット]"
desc="白色"
;;
zeusu)
card=59
text=$chara_bc
title="[ゼウス]"
desc="青色"
;;
esac
host_card=https://card.syui.ai/json/card.json
host_card_json=`curl -sL $host_card`
j=`echo $host_card_json|jq ".[]|select(.id == $card)"`
img=`echo $j|jq -r .img`
cp=$(($RANDOM % 1230))
s=super
skill=chara
link="https://card.syui.ai/$username"
card_check=`echo $data_card|jq -r ".[]|select(.card == $card)"`
if [ -z "$card_check" ];then
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\",\"skill\":\"$skill\"}" -s $host/cards`
card=`echo $tmp|jq -r .card`
cp=`echo $tmp|jq -r .cp`
fi
tmp_atr=`$atr reply-og "$text" --cid $cid --uri $uri --img $img --title "$title" --description "$desc" --link $link`
uri_post=`echo $tmp_atr|jq -r .uri|cut -d / -f 5`
post_url="https://bsky.app/profile/$yui_did/post/$uri_post"
ccid=`echo $tmp|jq -r .id`
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"url\":\"$post_url\",\"token\":\"$token\"}" $host/cards/$ccid`
exit
}
function chara_plus() {
case $ten_kai in
2)
chara_q=$chara_qb
;;
3)
chara_q=$chara_qc
;;
#4)
# chara_q=$chara_qd
# ;;
esac
echo "$chara_q"
case $1 in
1|2|3)
tmp_su=$1
ten_su=$((ten_su + $tmp_su))
;;
esac
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"ten_su\":$ten_su, \"ten_kai\":$ten_kai, \"token\":\"$token\"}" -s $host/users/$uid`
case $ten_kai in
4)
if [ $ten_su -eq 9 ] || [ $ten_su -eq 8 ] || [ $ten_su -eq 7 ];then
chara=ponta
elif [ $ten_su -eq 6 ];then
chara=octo
else
chara=zeusu
fi
chara_post $chara
;;
esac
}
chara_check
case "$option" in
start)
chara_start
;;
1|2|3)
ten_kai=$((ten_kai + 1))
chara_plus $option
;;
*)
echo "/chara start"
echo "/chara 1,2,3"
;;
esac
exit

View File

@@ -1,156 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
egg_card=40
handle=$1
did=$2
username=`echo $1|cut -d . -f 1`
opt=$3
if [ -z "$opt" ];then
echo no option
exit
fi
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
aiten=`echo $data|jq -r .aiten`
fav=`echo $data|jq -r .fav`
day_at=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 days ago'`
ten_at_n=`date --iso-8601=seconds`
d=`date +"%Y%m%d"`
limit_reset_at=`date --iso-8601=seconds -d '1 days ago'`
data_user_card=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"`
opt_dec=`echo $opt|base64 -d`
if [ "$did" = "$opt_dec" ];then
echo verify
else
echo no verify
exit
fi
fav_card=`echo $data_user_card|jq -r ".[]|select(.card == $egg_card)"`
cid=`echo $fav_card|jq -r .id`
egg_at=`echo $data|jq -r .egg_at`
egg_at=`date -d "$egg_at" +"%Y%m%d"`
egg_at_n=`date --iso-8601=seconds`
day_m=`date +"%H%M"`
day_mm=`date +"%H%M" -d "-1 min"`
day_mmm=`date +"%H%M" -d "-2 min"`
if [ -z "$fav_card" ];then
echo "no egg"
if [ "$egg_at" = "$d" ];then
echo "limit egg"
exit
fi
card=39
data_uu=`curl -sL "$host/users/$uid/card?itemsPerPage=2000"`
card_check=`echo $data_uu|jq -r ".[]|select(.card == $card)|.cp"|head -n 1`
if [ -n "$card_check" ];then
cp=$card_check
cid=`echo $data_uu|jq -r ".[]|select(.card == $card)|.id"|head -n 1`
echo "you already have, dragon"
ran=`echo $(($RANDOM % 3))`
ran_a=`echo $(($RANDOM % 5 + 1))`
if [ $ran -eq 1 ];then
card_check=$((card_check * 3))
echo "🐉 ---> $cp +${ran_a}"
cp=$((cp + ran_a))
else
cp=$((cp + 1))
echo "$cp +1"
fi
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"cp\":$cp,\"token\":\"$token\"}" $host/cards/$cid`
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"egg_at\":\"$egg_at_n\", \"token\":\"$token\"}" -s $host/users/$uid`
exit
fi
card=42
cp=0
data_uu=`curl -sL "$host/users/$uid/card?itemsPerPage=2000"`
card_check=`echo $data_uu|jq -r ".[]|select(.card == $card)|.cp"|head -n 1`
if [ -n "$card_check" ];then
echo "you already have, nyan"
ran=`echo $(($RANDOM % 1000 + 1000))`
aiten_p=$((aiten + ran))
echo "🐈 ---> [aiten]${aiten} +${ran}"
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"egg_at\":\"$egg_at_n\", \"aiten\":$aiten_p, \"token\":\"$token\"}" -s $host/users/$uid`
exit
fi
exit
fi
card_id=`echo $fav_card|jq -r ".id"`
card_cp=`echo $fav_card|jq -r ".cp"`
card_name=`echo $fav_card|jq -r ".card"`
card_status=`echo $fav_card|jq -r ".status"`
card_skill=`echo $fav_card|jq -r ".skill"`
function fav_status() {
echo "\n[card] ${card_name}"
echo "---"
echo "cp : ${card_cp}"
echo "cid : ${cid}"
echo "skill : ${card_skill}"
echo "status : ${card_status}"
}
function fav_battle() {
cp_b=`echo $(($RANDOM % 14))`
if [ "$egg_at" = "$d" ];then
echo "limit egg"
exit
fi
cp_i=`echo $fav_card|jq -r ".cp"`
card_name=`echo $fav_card|jq -r ".card"`
card_status=`echo $fav_card|jq -r ".status"`
card_skill=`echo $fav_card|jq -r ".skill"`
if [ $cp_i -ge $cp_b ];then
card=39
skill=dragon
cp=`echo $(($RANDOM % 1000 + 1200))`
s=third
ran=`echo $(($RANDOM % 10))`
if [ $ran -eq 1 ];then
card=42
skill=nyan
cp=0
fi
body="...congratulations! your egg has evolved\negg ---> ${skill} !!"
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"card\": $card,\"cp\":$cp,\"token\":\"$token\", \"status\": \"$s\",\"skill\": \"$skill\"}" $host/cards/$cid`
else
body="...no evolved"
fi
echo "\n${cp_i} vs $cp_b"
echo "----"
echo "${body}"
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"egg_at\":\"$egg_at_n\",\"token\":\"$token\"}" -s $host/users/$uid`
exit
}
fav_battle
exit

View File

@@ -1,161 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
handle=$1
did=$2
username=`echo $1|cut -d . -f 1`
opt=$3
if [ -z "$opt" ];then
echo no option
echo "---"
echo "CID = 1234567"
echo "@yui.syui.ai /fav 1234567"
echo "---"
echo "/fav status"
echo "/fav battle"
exit
fi
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
if [ $opt -eq 0 ];then
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"fav\": $opt,\"token\":\"$token\"}" -s $host/users/$uid`
echo ok
exit
fi
aiten=`echo $data|jq -r .aiten`
fav=`echo $data|jq -r .fav`
day_at=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 days ago'`
ten_at_n=`date --iso-8601=seconds`
d=`date +"%Y%m%d"`
limit_reset_at=`date --iso-8601=seconds -d '1 days ago'`
data_user_card=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"`
case "$opt" in
[bB]|-[bB]|[bB]attle|[sS]|-[sS]|[sS]tatus)
cid=`echo $data|jq -r .fav`
fav_card=`echo $data_user_card|jq -r ".[]|select(.id == $cid)"`
;;
*)
opt=$((opt + 0))
cid=$opt
fav_card=`echo $data_user_card|jq -r ".[]|select(.id == $cid)"`
;;
esac
updated_at=`echo $data|jq -r .updated_at`
updated_at_m=`date -d "$updated_at" +"%H%M"`
updated_at_n=`date --iso-8601=seconds`
updated_at=`date -d "$updated_at" +"%Y%m%d"`
raid_at=`echo $data|jq -r .raid_at`
raid_at=`date -d "$raid_at" +"%Y%m%d"`
raid_at_n=`date --iso-8601=seconds`
day_m=`date +"%H%M"`
day_mm=`date +"%H%M" -d "-1 min"`
day_mmm=`date +"%H%M" -d "-2 min"`
if [ -z "$fav_card" ];then
echo "no card id"
exit
fi
card_id=`echo $fav_card|jq -r ".id"`
card_cp=`echo $fav_card|jq -r ".cp"`
card_name=`echo $fav_card|jq -r ".card"`
card_status=`echo $fav_card|jq -r ".status"`
card_skill=`echo $fav_card|jq -r ".skill"`
function fav_status() {
echo "\n[card] ${card_name}"
echo "---"
echo "cp : ${card_cp}"
echo "cid : ${cid}"
echo "skill : ${card_skill}"
echo "status : ${card_status}"
}
function fav_battle() {
if [ -n "$1" ];then
cp_b=$1
else
cp_b=$(($RANDOM % 1400))
fi
if [ $updated_at -ge $d ] || [ "$updated_at" = "$d" ];then
echo "limit battle"
exit
fi
cp_i=`echo $fav_card|jq -r ".cp"`
card_name=`echo $fav_card|jq -r ".card"`
card_status=`echo $fav_card|jq -r ".status"`
card_skill=`echo $fav_card|jq -r ".skill"`
if [ $cp_i -gt $cp_b ];then
if [ $cp_i -ge 9000 ];then
cp_plus=$(($RANDOM % 27 + 1))
elif [ $cp_i -ge 7000 ];then
cp_plus=$(($RANDOM % 47 + 1))
else
cp_plus=$(($RANDOM % 237 + 1))
fi
else
cp_plus=$(($RANDOM % 17 + 1))
fi
echo "\n✧${cp_i} vs $cp_b"
echo "----"
cp=$((cp_i + cp_plus))
body="level up!"
echo "${body}${cp}(+${cp_plus})"
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"cp\":$cp,\"token\":\"$token\"}" $host/cards/$cid`
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"updated_at\":\"$updated_at_n\",\"token\":\"$token\"}" -s $host/users/$uid`
exit
}
function fav_add() {
card_status=fifth
u_data=`curl -sL "https://api.syui.ai/users/$uid/card?itemsPerPage=2555"|jq -r ".[]|select(.status == \"$card_status\")"`
if [ -z "$u_data" ];then
d_data=`curl -sL $host/cards/$cid|jq -r "select(.status == \"first\" or .status == \"second\" or .status == \"third\" or .status == \"yui\" or .status == \"fourth\" or .status == \"$card_status\")"`
if [ -z "$d_data" ];then
echo status $card_status
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"status\":\"$card_status\",\"token\":\"$token\"}" $host/cards/$cid`
fi
fi
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"fav\": $opt,\"token\":\"$token\"}" -s $host/users/$uid`
if [ -n "$tmp" ];then
echo ok
fi
exit
}
case "$opt" in
[bB]|-[bB]|[bB]attle)
fav_battle $4
;;
[sS]|-[sS]|[sS]tatus)
fav_status
;;
*)
fav_add
;;
esac
exit

View File

@@ -1,278 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
pass=`cat $HOME/.config/atr/api_card.json|jq -r .password`
token=`cat $HOME/.config/atr/api_card.json|jq -r .token`
handle=$1
did=$2
username=`echo $1|cut -d . -f 1`
cid=`echo $3|cut -d ' ' -f 1`
guser=$4
if [ "$3" = "ai" ];then
guser=ai
fi
if [ -z "$cid" ];then
echo no option
echo "---"
echo "@yui.syui.ai /gift ai"
echo "---"
echo "@yui.syui.ai /gift status"
echo "12345"
echo "67891"
echo "---"
echo "@yui.syui.ai /gift 12345"
echo ""
echo "---"
echo "@yui.syui.ai /gift 12345 syui"
exit
fi
function card_env(){
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
gdata=`echo $all_data|jq ".[]|select(.username == \"$guser\")"`
if [ -z "$data" ];then
exit
fi
uid=`echo $data|jq -r .id`
gid=`echo $gdata|jq -r .id`
aiten=`echo $data|jq -r .aiten`
fav=`echo $data|jq -r .fav`
cdata=`curl -sL $host/cards/$cid`
if [ -z "$cdata" ];then
echo no card
exit
fi
card=`echo $cdata|jq -r .card`
cp=`echo $cdata|jq -r .cp`
count=`echo $cdata|jq -r .count`
author=`echo $cdata|jq -r .author`
skill=`echo $cdata|jq -r .skill`
s=`echo $cdata|jq -r .status`
if [ $count -eq 0 ];then
echo card count 0
exit
fi
if [ $author != "$username" ];then
echo no author
echo "$author --> $username"
exit
fi
}
function card_env_ai(){
guser=ai
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
gdata=`echo $all_data|jq ".[]|select(.username == \"$guser\")"`
if [ -z "$data" ];then
exit
fi
uid=`echo $data|jq -r .id`
gid=`echo $gdata|jq -r .id`
aiten=`echo $data|jq -r .aiten`
fav=`echo $data|jq -r .fav`
cdata=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"|jq ".[0]"`
if [ -z "$cdata" ];then
cdata=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"|jq ".[]|select(.author == \"$username\")"|jq -s ".[0]"`
fi
if [ -z "$cdata" ];then
echo no card
exit
fi
cid=`echo $cdata|jq -r .id`
card=`echo $cdata|jq -r .card`
cp=`echo $cdata|jq -r .cp`
count=`echo $cdata|jq -r .count`
author=`echo $cdata|jq -r .author`
skill=`echo $cdata|jq -r .skill`
s=`echo $cdata|jq -r .status`
if [ $count -eq 0 ];then
echo card count 0
exit
fi
if [ $author != "$username" ];then
echo no author
echo "$author --> $username"
exit
fi
aicard=`curl -sL "$host/users/$gid/card?itemsPerPage=3000"|jq -r ".[]|select(.card >= 1)"|jq -s`
if [ -z "$aicard" ];then
exit
fi
n=`echo $aicard|jq length`
n=$((n - 1))
ran=$((RANDOM % n))
ai_id=`echo $aicard|jq -r ".[$ran]|.id"`
ai_card=`echo $aicard|jq -r ".[$ran]|.card"`
ai_cp=`echo $aicard|jq -r ".[$ran]|.cp"`
ai_skill=`echo $aicard|jq -r ".[$ran]|.skill"`
ai_s=`echo $aicard|jq -r ".[$ran]|.status"`
ai_author=ai
}
function card_env_ai_select(){
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
gdata=`echo $all_data|jq ".[]|select(.username == \"$guser\")"`
if [ -z "$data" ];then
exit
fi
uid=`echo $data|jq -r .id`
gid=`echo $gdata|jq -r .id`
aiten=`echo $data|jq -r .aiten`
fav=`echo $data|jq -r .fav`
cdata=`curl -sL $host/cards/$cid`
if [ -z "$cdata" ];then
echo no card
exit
fi
cid=`echo $cdata|jq -r .id`
card=`echo $cdata|jq -r .card`
cp=`echo $cdata|jq -r .cp`
count=`echo $cdata|jq -r .count`
author=`echo $cdata|jq -r .author`
skill=`echo $cdata|jq -r .skill`
s=`echo $cdata|jq -r .status`
if [ $count -eq 0 ];then
echo card count 0
exit
fi
if [ $author != "$username" ];then
echo no author
echo "$author --> $username"
exit
fi
aicard=`curl -sL "$host/users/$gid/card?itemsPerPage=3000"|jq -r ".[]|select(.card >= 1)"|jq -s`
if [ -z "$aicard" ];then
exit
fi
n=`echo $aicard|jq length`
n=$((n - 1))
ran=$((RANDOM % n))
ai_id=`echo $aicard|jq -r ".[$ran]|.id"`
ai_card=`echo $aicard|jq -r ".[$ran]|.card"`
ai_cp=`echo $aicard|jq -r ".[$ran]|.cp"`
ai_skill=`echo $aicard|jq -r ".[$ran]|.skill"`
ai_s=`echo $aicard|jq -r ".[$ran]|.status"`
ai_author=ai
}
function card_gift() {
card_env
if [ -z "$guser" ];then
echo card:$card
echo skill:$skill
echo status:$s
echo count:$count
echo author:$author
exit
fi
count=$((count - 1))
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$gid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\",\"skill\":\"$skill\",\"author\":\"$username\",\"count\":0}" -sL $host/cards`
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"count\":$count,\"token\":\"$token\"}" $host/cards/$cid -sL`
echo ok
echo "$author($cid) --> $guser"
}
function card_ai() {
card_env_ai
count=$((count - 1))
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$gid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\",\"skill\":\"$skill\",\"author\":\"$username\",\"count\":0}" -sL $host/cards`
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"count\":$count,\"token\":\"$token\"}" $host/cards/$cid -sL`
echo ok
echo "$author($cid) --> $guser"
echo "---"
echo 'thx!'
echo card:$ai_card
echo cp:$ai_cp
echo author:$ai_author
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"card\":$ai_card,\"status\":\"$ai_s\",\"cp\":$ai_cp,\"password\":\"$pass\",\"skill\":\"$ai_skill\",\"author\":\"$ai_author\",\"count\":0}" -sL $host/cards`
}
function card_ai_select() {
card_env_ai_select
count=$((count - 1))
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$gid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\",\"skill\":\"$skill\",\"author\":\"$username\",\"count\":0}" -sL $host/cards`
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"count\":$count,\"token\":\"$token\"}" $host/cards/$cid -sL`
echo ok
echo "$author($cid) --> $guser"
echo "---"
echo 'thx!'
echo card:$ai_card
echo cp:$ai_cp
echo author:$ai_author
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"card\":$ai_card,\"status\":\"$ai_s\",\"cp\":$ai_cp,\"password\":\"$pass\",\"skill\":\"$ai_skill\",\"author\":\"$ai_author\",\"count\":0}" -sL $host/cards`
}
function card_status(){
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
acard=`curl -sL "$host/users/$uid/card?itemsPerPage=3000"|jq ".[]|select(.author == \"$username\")|.id"`
if [ -z "$acard" ];then
echo no card
exit
fi
echo $acard
}
function test_cmd(){
echo "test ok /gift $1"
echo cid:$cid
echo guser:$guser
exit
}
case $cid in
"status")
card_status
;;
"ai")
#test_cmd ai
card_ai
;;
*)
if [ "ai" = "$guser" ];then
#test_cmd ai_select
card_ai_select
else
#test_cmd gift user
card_gift
fi
;;
esac
exit

File diff suppressed because it is too large Load Diff

View File

@@ -1,183 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
card_pay=$HOME/.config/ai/scpt/card_pay.zsh
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
host_card=https://card.syui.ai/json/card.json
host_card_json=`curl -sL $host_card`
n_cid=$HOME/.config/ai/txt/tmp_notify_cid.txt
f_cfg=$HOME/.config/ai/txt/tmp_ten_config.txt
handle=$1
did=$2
username=`echo $1|cut -d . -f 1`
cid=$3
uri=$4
if [ ! -f $f_cfg ];then
echo $host_card_json |jq -r ".[]|select(.ten != null)|.ten" |tr -d '\n' >! $f_cfg
fi
if [ -f $f_cfg ];then
nn=`cat $f_cfg|wc -c`
fi
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
function ten_yak() {
unset ran_a
unset ran_b
unset ran_c
unset ten_new
unset ten_yaku
ran_a=$(($RANDOM % nn))
ran_b=$(($RANDOM % nn))
ran_c=$(($RANDOM % nn))
ten_new=0
char_a=`cat $f_cfg| cut -c $ran_a`
char_b=`cat $f_cfg| cut -c $ran_b`
char_c=`cat $f_cfg| cut -c $ran_c`
ten_char=`echo "${char_a}\n${char_b}\n${char_c}"|head -n 3|sort|tr -d '\n'`
if [ ${#ten_char} -eq 0 ];then
ten_char=AAA
fi
if [ ${#ten_char} -eq 1 ];then
ten_char=AA${ten_char}
fi
if [ ${#ten_char} -eq 2 ];then
ten_char=A${ten_char}
fi
char_a=`echo $ten_char|cut -b 1`
char_b=`echo $ten_char|cut -b 2`
char_c=`echo $ten_char|cut -b 3`
case $ten_char in
EMY)
card=1
;;
KOS)
card=2
;;
CHI)
card=3
;;
AIT)
card=4
;;
OYZ)
card=5
;;
IKY)
card=6
;;
AKM)
card=7
;;
KUY)
card=8
;;
AW*)
card=9
;;
AHK)
card=10
;;
IKT)
card=11
;;
AAM)
card=12
;;
OSZ)
card=13
;;
CHO)
card=14
;;
*)
card=0
;;
esac
ten_new=${card}00
if [ $ten_new -eq 0 ];then
ten_new=0
else
ten_yaku="[$ten_char]"
fi
if [ "$ten_char" = "AAA" ];then
ten_new=100
fi
if [ "$char_a" = "A" ] && [ "$char_b" = "I" ] && [ $ten_new -ne 0 ];then
ten_new=150
fi
if [ "$char_a" = "$char_b" ] && [ $ten_new -ne 0 ];then
ten_new=50
fi
echo "[$i] $ten_su $ten_yaku+$ten_new"
ten_su=$((ten_su + ten_new))
}
function user_env() {
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
ten_data=`echo $all_data|jq ".|sort_by(.ten_su)|reverse|.[]|select(.ten_su != 0)"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
aiten=`echo $data|jq -r .aiten`
ten_post=`echo $data|jq -r .ten_post`
ten_bool=`echo $data|jq -r .ten`
day_at=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 days ago'`
ten_at_n=`date --iso-8601=seconds`
limit_reset_at=`date --iso-8601=seconds -d '1 days ago'`
d=`date +"%Y-%m-%d"`
ten_at=`echo $data|jq -r .ten_at`
ten_at=`date -d "$ten_at" +"%Y-%m-%d"`
ten_kai=`echo $data|jq -r .ten_kai`
if [ "$d" = "$ten_at" ];then
echo "limit aiten"
exit
fi
ten_kai=`echo $data|jq -r .ten_kai`
}
function ten_shutdown(){
ten_kai=0
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
ten=`echo $((ten_su + 200))`
ten_su=$ten
aiten=`echo $((aiten + ten_su))`
echo "+100"
echo "---"
echo user : $handle
echo ten : $ten
echo aiten :
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"ten_kai\":$ten_kai, \"ten_su\":$ten_su, \"ten\": false, \"token\":\"$token\", \"ten_at\" : \"$ten_at_n\", \"aiten\": $aiten}" -s $host/users/$uid`
}
user_env
for ((i=1;i<=7;i++))
do
ten_yak
done
ten_shutdown
exit

View File

@@ -1,31 +0,0 @@
#!/bin/zsh
host=https://api.syui.ai
pass=`cat $HOME/.config/atr/api_card.json|jq -r .password`
token=`cat $HOME/.config/atr/api_card.json|jq -r .token`
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
function ten_su_reset() {
ten_data=`curl -sL "$host/users?itemsPerPage=3000"|jq ".|sort_by(.ten_su)|reverse|.[]|select(.ten_su != 0)"`
ten_u_tmp=`echo $ten_data|jq -s`
ten_n_tmp=`echo $ten_u_tmp|jq "length"`
ten_n_tmp=$((ten_n_tmp - 1))
#ten_n_tmp=5
for ((i=0;i<$ten_n_tmp;i++))
do
u_i=`echo $ten_u_tmp|jq -r ".[$i].id"`
u_a=`echo $ten_u_tmp|jq -r ".[$i].username"`
u_s=`echo $ten_u_tmp|jq -r ".[$i].ten_su"`
echo "---"
echo "id : $u_i"
echo "user : $u_a"
echo "ten : $u_s"
curl -X PATCH -H "Content-Type: application/json" -d "{\"ten\": false,\"token\":\"$token\", \"ten_su\": 0}" -s $host/users/$u_i
done
}
ten_su_reset

View File

@@ -1,79 +0,0 @@
#!/bin/zsh
home=/root
name=arch
a=$home/$name
function arch_in(){
mkdir -p $a
sudo pacstrap -c $a base
#sudo echo pts/0 >> $a/etc/securetty
#sudo echo pts/1 >> $a/etc/securetty
sudo rm -rf /var/lib/machines/$name
sudo rm -rf /var/lib/machines/${name}back
sudo mv $a /var/lib/machines/
sudo machinectl clone arch archback
}
function arch_rm(){
sudo machinectl remove $name
}
function arch_up(){
sudo machinectl poweroff $name > /dev/null 2>&1
sleep 5
sudo machinectl terminate $name > /dev/null 2>&1
sleep 5
sudo machinectl start ${name}back
sleep 5
ssh ${name}back pacman -Syu --noconfirm
sleep 5
sudo machinectl poweroff ${name}back
}
function arch_st(){
sudo machinectl start $name
}
function arch_of(){
sudo machinectl poweroff $name
}
function arch_ex(){
sudo machinectl shell $name
$1
poweroff
}
function arch_re(){
sudo machinectl poweroff $name > /dev/null 2>&1
sleep 5
sudo machinectl terminate $name > /dev/null 2>&1
sleep 5
sudo machinectl remove $name
sleep 5
sudo machinectl clone ${name}back $name
sleep 5
sudo machinectl start $name
}
case "$1" in
"update"|"-u")
arch_up
arch_re
echo "machinectl update done"
;;
"reset"|"-r")
arch_re
echo "machinectl reset done"
;;
*)
sudo machinectl start $name
t=`ssh $name "$*"`
if [ -z "$t" ];then
ssh -tt $name "$*"
else
echo "$t"
fi
;;
esac

View File

@@ -1,21 +0,0 @@
#!/bin/zsh
dir=${0:a:h}
f=$HOME/.config/ai/scpt/png/t.png
f_sleep=$HOME/.config/ai/scpt/png/sleep
cid=$1
uri=$2
if [ ! -f $f ];then
atr r "limit 1 day" -c $cid -u $uri
exit
fi
if [ -f $f_sleep ];then
link=bafkreidgp2cl4cvkn3i4gzqj6kfiwngjjh5ie2jwobh632jh4ejlbiwdhm
atr img-post "#nyancat" -l $link -c $cid -u $uri
rm $f_sleep
exit
fi
link=`atr img-upload $f|jq -r .blob.ref.'"$link"'`
atr img-post "#stablediffusion" -l $link -c $cid -u $uri

View File

@@ -1,21 +0,0 @@
#!/bin/zsh
d=${0:a:h:h}
url="search.bsky.social/search/posts?q="
q=`echo $*|tr ' ' '+'`
url="${url}${q}"
t=`curl -sL "$url"`
echo $t
n=`echo $t|jq "length"`
n=`expr $n - 1`
for ((i=0;i<=$n;i++))
do
did=`echo $t|jq -r ".[$i].user.did"`
text=`echo $t|jq -r ".[$i].post.text"`
tid=`echo $t|jq -r ".[$i].tid"`
if [ "$text" != "null" ];then
echo $did
echo $text
echo https://$tid
fi
done

View File

@@ -1,24 +0,0 @@
#!/bin/zsh
d=${0:a:h}
dd=${0:a:h:h}/json
#https://github.com/bluesky-social/atproto/issues/597
host=bsky.social
base=https://$host/xrpc
handle=`cat ~/.config/atr/token.json| jq -r .handle`
token=`cat ~/.config/atr/token.json| jq -r .accessJwt`
if [ -n "$1" ];then
url=$base/$1
else
url="$base/app.bsky.actor.getProfile?actor=$handle"
fi
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $token" $url | jq . >! $dd/profile.json
cat $dd/profile.json
url=$base/app.bsky.feed.getTimeline
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $token" $url | jq . >! $dd/timeline.json
cat $dd/timeline.json
url=$base/app.bsky.notification.listNotifications
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $token" $url | jq . >! $dd/notify.json
cat $dd/notify.json|jq .

View File

@@ -1,39 +0,0 @@
#!/bin/zsh
host_at=bsky.social
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
scpt=$HOME/.config/ai/scpt/api_card.zsh
handle=skychan.social
did=did:plc:7hgow77uky7lgbinwyvbzhar
f=$HOME/.config/ai/card_bot.txt
touch $f
n=5
for ((i=0;i<=$n;i++))
do
echo $i
cid=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post" |jq -r ".[]|.[$i]?|.cid"`
uri=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post" |jq -r ".[]|.[$i]?|.uri"`
t=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post" |jq -r ".[]|.[$i]?|.value.text"`
echo $t $cid $uri
if [ "@yui.syui.ai /card" = "$t" ];then
if [ "$cid" = "`cat $f`" ];then
exit
fi
card=`$scpt $handle $did`
link="https://card.syui.ai/skychan"
~/.cargo/bin/atr r "$card" -c $cid -u "$uri" -l "$link"
echo $cid >! $f
fi
if [ "@yui.syui.ai /card -b" = "$t" ];then
if [ "$cid" = "`cat $f`" ];then
exit
fi
card=`$scpt $handle $did -b`
link="https://card.syui.ai/skychan"
~/.cargo/bin/atr r "\n$card" -c $cid -u "$uri" -l "$link"
echo $cid >! $f
fi
done
exit

View File

@@ -1,60 +0,0 @@
#!/bin/zsh
d=$HOME/.config/ai/txt
mkdir -p $d
f=$d/kawaii.txt
function test_post() {
host_at=bsky.social
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
handle=lilly-niyu.bsky.social
n=10
for ((i=0;i<=$n;i++))
do
echo $i
cid=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post" |jq -r ".[]|.[$i]?|.cid"`
uri=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post" |jq -r ".[]|.[$i]?|.uri"`
t=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post" |jq -r ".[]|.[$i]?|.value.text"`
echo $t $cid $uri
if [ "かわいいにゃ〜!!" = "$t" ];then
#atr r "楽しそう!アイもまぜてよ" -c $cid -u "$uri"
fi
done
}
#uri=at://$did/$tid
#{
# "tid": "app.bsky.feed.post/3judrtmxuin2b",
# "cid": "bafyreibebbn7og5dgvvegcjlh357pnrpuvvinbjeaqbfvqb4jsrpbsw3di",
# "user": {
# "did": "did:plc:hodycxjeqfxtest2ilj47j7g",
# "handle": "lilly-niyu.bsky.social"
# },
# "post": {
# "createdAt": 1682587582509000000,
# "text": "かわいいにゃ〜!!",
# "user": "lilly-niyu.bsky.social"
# }
#}
url="search.bsky.social/search/posts?q=かわいいにゃ〜!!"
q=`echo $*|tr ' ' '+'`
url="${url}${q}"
t=`curl -sL "$url"`
cid=`echo $t|jq -r ".[1].cid"`
did=`echo $t|jq -r ".[1].did"`
tid=`echo $t|jq -r ".[1].tid"`
uri="at://$did/$tid"
text=`echo $t|jq -r ".[1].post.text"`
touch $f
echo $text
if [ "かわいいにゃ〜!!" = "$text" ] && [ "`cat $f`" != "$cid" ];then
echo ok
atr r "楽しそう!アイもまぜてよ" -c $cid -u "$uri"
echo $cid >! $f
else
echo no
fi

View File

@@ -1,79 +0,0 @@
#!/bin/zsh
url_plc="https://plc.directory/export"
host_at=bsky.social
url=https://plc.directory
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
dir=$HOME/.config/atr/txt
file=$dir/bot_list.txt
unset timed
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
if [ -z "$1" ];then
exit
fi
if ! echo $1|grep "." >/dev/null 2>&1;then
echo "ex : user syui.bsky.social"
exit
fi
if [ ! -d $dir ];then
mkdir -p $dir
fi
if [ ! -f $file ];then
touch $file
fi
function mfile() {
t=`cat $file|sort|uniq`
if [ -n "$t" ];then
echo "$t" >! ${file}.back
mv ${file}.back $file
fi
}
function plc(){
if cat $file|grep "$1" >/dev/null 2>&1;then
cat $file|grep "$1"
exit
fi
json_tmp=`curl -sL "${url_plc}?after=${timed}"|jq .`
json=`echo $json_tmp|jq "select(.operation.handle == \"$1\")"`
if [ -z "$json" ];then
check=`echo $json_tmp|jq -r ".operation.alsoKnownAs"|head -n 1`
if [ "null" != "$check" ];then
json=`echo $json_tmp|jq "select(.operation.alsoKnownAs|.[] == \"at://$1\")"` >/dev/null 2>&1
fi
fi
if [ -n "$json" ];then
created_at=`echo $json|jq -r .createdAt |tail -n 1`
fi
if [ -n "$created_at" ];then
echo "$created_at : $1"
echo "$created_at : $1" >> $file
mfile
exit
fi
}
if [ "$1" = "-l" ];then
mfile
cat $file
exit
fi
for ((i=0;i<=20;i++))
do
if [ $i -eq 0 ];then
timed="1970-01-01"
fi
plc $1
timed=`echo $json_tmp|jq -r .createdAt|tail -n 1`
done

View File

@@ -1,29 +0,0 @@
#!/bin/zsh
d=${0:a:h}
dd=${0:a:h:h}/json
#https://github.com/bluesky-social/atproto/issues/597
host=`cat ~/.config/atr/config.json|jq -r .host`
base=https://$host/xrpc
handle=`cat ~/.config/atr/config.json|jq -r .user`
pass=`cat ~/.config/atr/config.json|jq -r .pass`
f=~/.config/atr/token.json
curl -X POST -H "Content-Type: application/json" -d "{\"identifier\":\"$handle\",\"password\":\"$pass\"}" https://$host/xrpc/com.atproto.server.createSession | jq . >! $f
cat $f
if [ "$1" = "-a" ];then
handle=`cat $f| jq -r .handle`
token=`cat $f| jq -r .accessJwt`
url="$base/app.bsky.actor.getProfile?actor=$handle"
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $token" $url | jq . >! $dd/profile.json
cat $dd/profile.json
url=$base/app.bsky.feed.getTimeline
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $token" $url | jq . >! $dd/timeline.json
cat $dd/timeline.json
url=$base/app.bsky.notification.listNotifications
curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer $token" $url | jq . >! $dd/notify.json
cat $dd/notify.json|jq .
fi

View File

@@ -1,40 +0,0 @@
#!/bin/zsh
echo "not open"
exit
atr=$HOME/.cargo/bin/atr
url_j=https://card.syui.ai/json/card.json
tcid=$HOME/.config/ai/txt/tmp_notify_cid.txt
handle=$1
did=$2
cid=$3
uri=$4
if [ ! -d $HOME/.config/ai/txt ];then
mkdir -p $HOME/.config/ai/txt
fi
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
url=https://api.syui.ai
username=`echo $1|cut -d . -f 1`
link=https://card.syui.ai/$username
ran=$(($RANDOM % 10))
if [ $ran -eq 1 ];then
uranai="今日の運勢をルーン占いでやってください。結果を120文字以内で教えてください"
else
uranai="今日の運勢をタロット占いでやってください。結果を120文字以内で教えてください"
fi
body=`$atr chat "$uranai" -c`
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
exit

View File

@@ -1,161 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
url_j=https://card.syui.ai/json/card.json
tcid=$HOME/.config/ai/txt/tmp_notify_cid.txt
handle=$1
did=$2
cid=$3
uri=$4
if [ ! -d $HOME/.config/ai/txt ];then
mkdir -p $HOME/.config/ai/txt
fi
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
url=https://api.syui.ai
username=`echo $1|cut -d . -f 1`
link=https://card.syui.ai/$username
ran=$(($RANDOM % 10))
if [ $ran -eq 1 ];then
uranai="今日の運勢をルーン占いでやってください。結果を120文字以内で教えてください"
else
uranai="今日の運勢をタロット占いでやってください。結果を120文字以内で教えてください"
fi
uid=`curl -sL "$url/users?itemsPerPage=2000"|jq ".[]|select(.username == \"$username\")"|jq -r .id`
if [ -z $uid ] || [ "$uid" = "null" ];then
body=`$atr chat "$uranai" -c`
body=`echo "占いにはアイのカードが3枚以上必要です\n\n$body"`
if [ "`cat $tcid`" != "$cid" ];then
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
fi
exit
fi
data=`curl -sL "$url/users/$uid"`
data_u=`curl -sL "$url/users/$uid/card?itemsPerPage=2000"`
luck_at=`echo $data|jq -r .luck_at`
luck_at_n=`date --iso-8601=seconds`
luck_at=`date -d "$luck_at" +"%Y%m%d"`
day_at=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 days ago'`
if [ "$luck_at" = "$day_at" ];then
body=`$atr chat "$uranai" -c`
if [ "`cat $tcid`" != "$cid" ];then
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
fi
exit
fi
cp_i=`echo $data_u |jq -r "sort_by(.cp) | reverse|.[].card"|sort|uniq|sed -e '1d'`
cp_n=`echo $cp_i|wc -l`
if [ 3 -gt $cp_n ];then
body=`$atr chat "$uranai" -c`
body=`echo "占いにはアイのカードが3枚以上必要です\n\n$body"`
if [ "`cat $tcid`" != "$cid" ];then
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
fi
exit
fi
ran=$(($RANDOM % $cp_n + 1))
luck=$(($RANDOM % 8))
sub=$(($RANDOM % 15))
card=`echo $cp_i|awk "NR==$ran"`
if [ $sub -eq $card ];then
sub="x2"
else
sub=`curl -sL $url_j|jq -r ".[]|select(.id == $sub)|.p"`
fi
j=`curl -sL $url_j|jq ".[]|select(.id == $card)"`
img=`echo $j|jq -r .img`
if [ -z $img ] || [ "$img" = "null" ];then
exit
fi
test_ran=`echo $(($RANDOM % 3))`
#text="アイ・カード占い"
case $test_ran in
1)
text=`$atr chat "アイのカード占いを作ってやってみて。感じる答えを出して。できれば140文字以内でお願い。" -c`
;;
2)
text=`$atr chat "今、アイが自分で考えた占いやってみて。気分が乗らなかったらやらなくていいよ。できれば140文字以内で答えてね。" -c`
;;
*)
text=`$atr chat "今のアイの気分で適当に占ってみて。できれば140文字以内で答えてね。" -c`
;;
esac
title=`echo $j|jq -r .h`
title="[${title}]"
#desc=`echo $j|jq -r .p`
if [ 0 -eq $luck ];then
desc="0"
fi
if [ 1 -eq $luck ];then
desc="1"
fi
if [ 2 -eq $luck ];then
desc="2"
fi
if [ 3 -eq $luck ];then
desc="3"
fi
if [ 4 -eq $luck ];then
desc="4"
fi
if [ 5 -eq $luck ];then
desc="5"
fi
if [ 6 -eq $luck ];then
desc="6"
fi
if [ 7 -eq $luck ];then
desc="7"
fi
desc=`echo "アイ数字は${luck}"`
if [ "`cat $tcid`" != "$cid" ];then
if $atr reply-og "$text" --cid $cid --uri $uri --img $img --title "$title" --description "$desc" --link $link;then
echo $cid >! $tcid
fi
fi
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
if [ $luck -eq 7 ];then
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"luck_at\":\"$luck_at_n\",\"token\":\"$token\",\"luck\": $luck, \"next\": \"$nd\"}" -s $url/users/$uid`
else
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"luck_at\":\"$luck_at_n\",\"token\":\"$token\",\"luck\": $luck}" -s $url/users/$uid`
fi
exit

View File

@@ -1,147 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
url_j=https://card.syui.ai/json/card.json
tcid=$HOME/.config/ai/txt/tmp_notify_cid.txt
handle=$1
did=$2
cid=$3
uri=$4
if [ ! -d $HOME/.config/ai/txt ];then
mkdir -p $HOME/.config/ai/txt
fi
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
url=https://api.syui.ai
username=`echo $1|cut -d . -f 1`
link=https://card.syui.ai/$username
ran=$(($RANDOM % 10))
if [ $ran -eq 1 ];then
uranai="Please do a of today's rune-fortune. Tell us the result in 120 characters or less."
else
uranai="Please do a of today's tarot-fortune. Tell us the result in 120 characters or less."
fi
uid=`curl -sL "$url/users?itemsPerPage=2000"|jq ".[]|select(.username == \"$username\")"|jq -r .id`
if [ -z $uid ] || [ "$uid" = "null" ];then
body=`$atr chat "$uranai" -c`
body=`echo "At least 3 cards of the Eye are required for divination\n\n$body"`
if [ "`cat $tcid`" != "$cid" ];then
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
fi
exit
fi
data=`curl -sL "$url/users/$uid"`
data_u=`curl -sL "$url/users/$uid/card?itemsPerPage=2000"`
luck_at=`echo $data|jq -r .luck_at`
luck_at_n=`date --iso-8601=seconds`
luck_at=`date -d "$luck_at" +"%Y%m%d"`
day_at=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 days ago'`
if [ "$luck_at" = "$day_at" ];then
body=`$atr chat "$uranai" -c`
if [ "`cat $tcid`" != "$cid" ];then
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
fi
exit
fi
cp_i=`echo $data_u |jq -r "sort_by(.cp) | reverse|.[].card"|sort|uniq|sed -e '1d'`
cp_n=`echo $cp_i|wc -l`
if [ 3 -gt $cp_n ];then
body=`$atr chat "$uranai" -c`
body=`echo "At least 3 cards of the Eye are required for divination\n\n$body"`
if [ "`cat $tcid`" != "$cid" ];then
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
fi
exit
fi
ran=$(($RANDOM % $cp_n + 1))
luck=$(($RANDOM % 8))
sub=$(($RANDOM % 15))
card=`echo $cp_i|awk "NR==$ran"`
if [ $sub -eq $card ];then
sub="x2"
else
sub=`curl -sL $url_j|jq -r ".[]|select(.id == $sub)|.p"`
fi
j=`curl -sL $url_j|jq ".[]|select(.id == $card)"`
img=`echo $j|jq -r .img`
if [ -z $img ] || [ "$img" = "null" ];then
exit
fi
text="AI Card Fortune Telling"
title=`echo $j|jq -r .h_en`
title="[${title}]"
#desc=`echo $j|jq -r .p`
if [ 0 -eq $luck ];then
desc="0"
fi
if [ 1 -eq $luck ];then
desc="1"
fi
if [ 2 -eq $luck ];then
desc="2"
fi
if [ 3 -eq $luck ];then
desc="3"
fi
if [ 4 -eq $luck ];then
desc="4"
fi
if [ 5 -eq $luck ];then
desc="5"
fi
if [ 6 -eq $luck ];then
desc="6"
fi
if [ 7 -eq $luck ];then
desc="7"
fi
desc=`echo "lucky number ${luck}"`
if [ "`cat $tcid`" != "$cid" ];then
if $atr reply-og "$text" --cid $cid --uri $uri --img $img --title "$title" --description "$desc" --link $link;then
echo $cid >! $tcid
fi
fi
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
if [ $luck -eq 7 ];then
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"luck_at\":\"$luck_at_n\",\"token\":\"$token\",\"luck\": $luck, \"next\": \"$nd\"}" -s $url/users/$uid`
else
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"luck_at\":\"$luck_at_n\",\"token\":\"$token\",\"luck\": $luck}" -s $url/users/$uid`
fi
exit

View File

@@ -1,154 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
host_card=https://card.syui.ai/json/card.json
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
handle=$1
username=`echo $1|cut -d . -f 1`
did=$2
cid=$3
uri=$4
pay=60000
echo $handle
function card_d(){
j=`curl -sL $host_card |jq -r ".[]|select(.ten_skill == true)"|jq -s`
n=`echo $j|jq length`
n_leng=$n
n=$(($RANDOM % n - 1))
card=`echo $j|jq -r ".[$n].id"`
img=`echo $j|jq -r ".[$n].img"`
ten=`echo $j|jq -r ".[$n].ten"`
title=`echo $j|jq -r ".[$n].h"`
title="[$title]"
ran_a=$(($RANDOM % 1000))
cp=$((ran_a + 500))
ran_s=$(($RANDOM % 5))
skill=ten
if [ $ran_s -eq 1 ];then
s=super
cp=$((cp + $ran_a))
else
s=normal
fi
#card=1;skill=3d;s=3d
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
aiten=`echo $data|jq -r .aiten`
model=`echo $data|jq -r .model`
model_mode=`echo $data|jq -r .model_mode`
model_attack=`echo $data|jq -r .model_attack`
model_skill=`echo $data|jq -r .model_skill`
model_limit=`echo $data|jq -r .model_limit`
model_critical=`echo $data|jq -r .model_critical`
model_critical_d=`echo $data|jq -r .model_critical_d`
ten_data=`echo $all_data|jq ".|sort_by(.aiten)|reverse|.[]|select(.aiten >= $pay)"`
model_critical=$((RANDOM % 10 + model_critical))
json_model="{\"model_critical\":$model_critical, \"token\":\"$token\"}"
body_d=`echo "[card]\nid : $card\ncp : $cp\nstatus : $s\nskill : $skill\n---\n[model]\ncritical : ${model_critical}%"`
}
function card_user(){
if [ -z "$ten_data" ] || [ -z "$aiten" ] || [ $aiten -le $pay ];then
echo "aiten : $aiten >= $pay [1/${n_leng}]"
exit
else
pay_s=$((aiten - pay))
if [ $pay_s -lt 0 ] || [ -z "$pay_s" ];then
echo "aiten : $aiten >= $pay [1/${n_leng}]"
exit
fi
body_user=`echo "${aiten} : $aiten - $pay = $pay_s [1/${n_leng}]"`
fi
}
function card_check(){
data_uu=`curl -sL "$host/users/$uid/card?itemsPerPage=2000"`
card_check=`echo $data_uu|jq -r ".[]|select(.card == $card)"`
card_check_skill=`echo $card_check|jq "select(.skill == \"$skill\")"`
if [ -n "$card_check" ] && [ -n "$card_check_skill" ];then
echo "$body_user"
echo "lost, you chose the card you already have..."
echo "ai[model] Lv up!"
s_up=$((RANDOM % 3 + 1))
case `echo $((RANDOM % 4))` in
0)
model_mode=$((model_mode + s_up))
json="{\"token\":\"$token\", \"model_mode\": $model_mode}"
echo "\"mode\": Lv${model_mode}"
;;
1)
model_attack=$((model_attack + s_up))
json="{\"token\":\"$token\", \"model_attack\": $model_attack}"
echo "\"attack\": Lv${model_attack}"
;;
2)
model_skill=$((model_skill + s_up))
json="{\"token\":\"$token\", \"model_skill\": $model_skill}"
echo "\"skill\": Lv${model_skill}"
;;
3)
model_limit=$((model_limit + s_up))
json="{\"token\":\"$token\", \"model_limit\": $model_limit}"
echo "\"burst\": Lv${model_limit}"
;;
*)
model_limit=$((model_limit + s_up))
json="{\"token\":\"$token\", \"model_limit\": $model_limit}"
echo "\"burst\": Lv${model_limit}"
;;
esac
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "$json" -s $host/users/$uid`
card=0
cp=1
s=super
skill=lost
#echo "try again next time!"
echo "[card]"
echo "id : $card"
echo "cp : $cp"
echo "skill : $skill"
if [ "$handle" != "ai" ];then
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"token\":\"$token\", \"aiten\": $pay_s}" -s $host/users/$uid`
fi
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\",\"skill\":\"$skill\"}" -s $host/cards`
exit
fi
}
function card_pay(){
link=https://card.syui.ai/$username
text=`echo "$body_user\n$body_d"`
desc="[$ten]"
if [ "$handle" != "ai" ];then
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"token\":\"$token\", \"aiten\": $pay_s, \"model_critical\": $model_critical}" -s $host/users/$uid`
else
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"token\":\"$token\", \"aiten\": 10000000, \"model_critical\": $model_critical}" -s $host/users/$uid`
echo $tmp
fi
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"card\":$card,\"status\":\"$s\",\"cp\":$cp,\"password\":\"$pass\",\"skill\":\"$skill\"}" -s $host/cards`
echo "$text"
#echo "$atr reply-og \"$text\" --cid $cid --uri $uri --img $img --title \"$title\" --description \"$desc\" --link $link"
#tmp_atr=`$atr reply-og "$text" --cid $cid --uri $uri --img $img --title "$title" --description "$desc" --link $link`
}
card_d
card_user
card_check
card_pay

View File

@@ -1,37 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
url_j=https://card.syui.ai/json/card.json
tcid=$HOME/.config/atr/txt/tmp_notify_cid.txt
handle=$1
did=$2
cid=$3
uri=$4
if [ ! -d $HOME/.config/atr/txt ];then
mkdir -p $HOME/.config/atr/txt
fi
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
url=https://api.syui.ai
username=`echo $1|cut -d . -f 1`
link=https://card.syui.ai/$username
ran=$(($RANDOM % 10))
if [ $ran -eq 1 ];then
uranai="今日の運勢をルーン占いでやってください。結果を120文字以内で教えてください"
else
uranai="今日の運勢をタロット占いでやってください。結果を120文字以内で教えてください"
fi
body=`$atr chat "$uranai" -c`
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
exit

View File

@@ -1,37 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
url_j=https://card.syui.ai/json/card.json
tcid=$HOME/.config/ai/txt/tmp_notify_cid.txt
handle=$1
did=$2
cid=$3
uri=$4
if [ ! -d $HOME/.config/ai/txt ];then
mkdir -p $HOME/.config/ai/txt
fi
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
url=https://api.syui.ai
username=`echo $1|cut -d . -f 1`
link=https://card.syui.ai/$username
ran=$(($RANDOM % 10))
if [ $ran -eq 1 ];then
uranai="Please do a of today's rune-fortune. Tell us the result in 120 characters or less."
else
uranai="Please do a of today's tarot-fortune. Tell us the result in 120 characters or less."
fi
body=`$atr chat "$uranai" -c`
if $atr r "$body" -c $cid -u $uri;then
echo $cid >! $tcid
fi
exit

View File

@@ -1,108 +0,0 @@
#!/bin/zsh
dir=${0:a:h}
mkdir -p $dir/png
f=$dir/png/t.png
f_sleep=$dir/png/sleep
cfg=$dir/stable_diffusion_prompt.j
cfg_did=$dir/stable_diffusion_did.txt
opt_af=$dir/stable_diffusion_a.txt
opt_bf=$dir/stable_diffusion_b.txt
opt_allf=$dir/stable_diffusion_all.txt
if [ ! -f $cfg_did ];then
touch $cfg_did
fi
did=$1
admin=did:plc:uqzpqmrjnptsxezjx4xuh2mn
if [ ! -f $cfg ];then
echo no file $cfg
exit
fi
opt_a=`echo $@|cut -d ' ' -f 2`
opt_b=`echo $@|cut -d ' ' -f 3`
#case "$opt_b" in
# bluesky|sky|field|girl|anime|universe|earth|bird|miku|ai|yui|card|blue|cat)
# ;;
# *)
# if [ -n "$opt_b" ] && [ "$did" != "$admin" ];then
# opt_b=nyancat
# fi
# ;;
#esac
echo $opt_a >! $opt_af
echo $opt_b >! $opt_bf
echo $@ >! $opt_allf
case "$opt_a" in
-p|p)
q="$opt_b , masterpiece, best quality, 8k wallpaper Highly, cinematic Lighting, cinematic Beautiful"
;;
-t|t)
tag=$opt_b
json=`cat $cfg|jq ".[]|select(.tag == \"${tag}\")"`
if [ -z "$json" ] || [ -z "$tag" ];then
echo no tag
exit
fi
json=`echo $json|jq -s`
n=`echo $json|jq "length"`
n=$((RANDOM % n))
q=`echo $json|jq -r ".[$n].body"`
#m=`echo $json|jq -r ".[$n].model"`
;;
*)
n=`cat $cfg|jq "length"`
n=$((RANDOM % n))
q=`cat $cfg|jq -r ".[$n].body"`
;;
esac
model_s="model
coharu
flat2d
pastelmix
pvcstyle"
model_r=$((RANDOM % `echo "$model_s"|wc -l` + 1))
m=`echo "$model_s"|awk "NR==$model_r"`
case $1 in
-pm)
m=$2
q=`echo $@ | cut -d ' ' -f 3-`
;;
esac
if [ -z "$q" ];then
echo no prompt
exit
fi
if [ -z "$m" ];then
m=model
fi
echo $q
echo $m
function run(){
if [ -f $f_sleep ];then
rm $f_sleep
fi
ssh ue "conda activate ldm;cd ./stable-diffusion/;python safe.py \'${q}\' ${m}"
scp -r ue:stable-diffusion/t.png $f
if [ $? -ne 0 ];then
touch $f_sleep
fi
}
if [ -n "`cat $cfg_did| grep -x $did`" ] && [ "$did" != "$admin" ];then
rm $f
else
echo "\n$did" >> $cfg_did
run
fi

View File

@@ -1,7 +0,0 @@
#!/bin/zsh
txt=_atproto.$1
did=$2
curl -sL plc.directory/$did/log |jq ".[]|.alsoKnownAs|.[]?" | grep -n .| head -n 4
dig -t TXT $txt|grep "did=did:plc:"|head -n 1

View File

@@ -1,145 +0,0 @@
#!/bin/zsh
url_plc="https://plc.directory/export"
host_at=bsky.social
url=https://plc.directory
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
dir=$HOME/.config/atr/txt
file=$dir/user_list.txt
opt=`echo $4|tr -d "'"`
dir_git_card_page=$HOME/git/card.syui.ai
if [ ! -d $dir_git_card_page ];then
mkdir -p $HOME/git
cd $HOME/git
git clone https://github.com/syui/card.syui.ai
else
cd $dir_git_card_page
t=`git pull`
fi
file_fanart=$dir_git_card_page/public/json/fanart.json
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
created_at=`date --iso-8601=seconds`
#if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ];then
# exit
#fi
function fan_art_search() {
k="aifanart"
url="search.bsky.social/search/posts?q="
t=`curl -sL "${url}${k}"|jq ".[]|select(.post.text == \"$k\")"`
#n=`curl -sL "${url}${k}"|jq length`
n=$((n - 1))
for ((i=0;i<=$n;i++))
do
did=`curl -sL "${url}${k}"|jq -r ".[$i]|.user.did"`
handle=`curl -sL https://plc.directory/$did|jq -r ".alsoKnownAs|.[]"|cut -d / -f 3-`
if [ -z "$handle" ];then
continue
fi
tid=`curl -sL "${url}${k}"|jq -r ".[$i]|.tid"|cut -d / -f 2`
http=https://staging.bsky.app/profile/$handle/post/$tid
echo $http
done
}
if [ "$1" = "-s" ];then
fan_art_search
exit
fi
if ! echo $1|grep "." >/dev/null 2>&1;then
echo "ex : user syui.bsky.social"
exit
fi
if ! echo $2|grep "did:plc:" >/dev/null 2>&1;then
echo "ex : user did"
exit
fi
if [ "$3" = "-l" ];then
curl -sL card.syui.ai/json/fanart.json|jq -r ".[]|.author, .link"
exit
fi
if ! echo $3|grep -e "bsky.app/profile/" -e "nostter.vercel.app" >/dev/null 2>&1;then
echo "please url : bsky.app/profile/$1/post/xxx"
exit
fi
if ! echo $4|grep -e "https://av-cdn.bsky.app/img" -e "https://nostr.build/i/" >/dev/null 2>&1;then
if [ "$opt" != "-d" ];then
echo "please url : av-cdn.bsky.social/img"
exit
fi
fi
function fan_art(){
add=$1
did=$2
if echo $3|grep "nostter.vercel.app" >/dev/null 2>&1;then
link=`echo $3|cut -d / -f 1-4`
else
link=$3
fi
img=`echo $4|tr -d "'"`
author=`echo $3|cut -d / -f 5`
if [ -z "$author" ];then
author="nostr"
fi
cd $dir_git_card_page
check_null=`cat $file_fanart|jq ".[]|select(.img == \"$img\")"`
if [ -n "$check_null" ];then
echo registered
exit
fi
echo `cat $file_fanart` "[{\"add\":\"$add\",\"link\":\"$link\",\"author\":\"$author\",\"img\":\"$img\",\"created_at\":\"$created_at\",\"did\":\"$did\"}]" | jq -s add >! $file_fanart.back
if cat $file_fanart.back|jq . >/dev/null 2>&1;then
mv $file_fanart.back $file_fanart
git add $file_fanart
git commit -m "add fanart"
git push -u origin main -f
echo add fanart, thx $1
echo "author : $author"
echo "it will take some time to deploy"
fi
}
function fan_art_delete(){
add=$1
did=$2
link=$3
cd $dir_git_card_page
check_null=`cat $file_fanart|jq ".[]|select(.img == \"$img\")"`
if [ -n "$check_null" ];then
echo registered
exit
fi
cat $file_fanart|jq ".[]|select(.link == \"$link\")|= .+{\"delete\":true,\"delete-did\":\"$did\"}"|jq -s >! $file_fanart.back
if cat $file_fanart.back|jq . >/dev/null 2>&1;then
mv $file_fanart.back $file_fanart
git add $file_fanart
git commit -m "delete fanart"
git push -u origin main -f
echo delete fanart, thx $1
echo "delete-did : $did"
echo "it will take some time to deploy"
fi
}
if [ "$opt" = "-d" ];then
fan_art_delete $1 $2 $3
exit
fi
fan_art $1 $2 $3 $4
exit

View File

@@ -1,75 +0,0 @@
#!/bin/zsh
d=$HOME/.config/ai/txt
mkdir -p $d
unset cursor
function page(){
s=$1
if [ "$s" = "ers" ];then
opt="-w"
elif [ "$s" = "s" ];then
opt="-s"
fi
f=$d/follow${s}_${ii}.json
echo $f
if [ -n "$cursor" ];then
if [ ! -f $f ];then
atr follow $opt -c $cursor| jq . >! $f
else
echo no download
fi
else
if [ ! -f $f ];then
atr follow $opt| jq . >! $f
else
echo no download
fi
fi
cursor=`cat $f|jq -r .cursor`
echo "------------------------------"
echo $cursor
echo "------------------------------"
n=`cat $f|jq ".follow${s}|length"`
n=`expr $n - 1`
for ((i=0;i<=$n;i++))
do
handle=`cat $f|jq -r ".follow${s}|.[$i].handle"`
did=`cat $f|jq -r ".follow${s}|.[$i].did"`
flg=`cat $f|jq -r ".follow${s}|.[$i].viewer.following"`
flb=`cat $f|jq -r ".follow${s}|.[$i].viewer.followedBy"`
if [ "$flg" = "null" ];then
echo following
echo $flb
echo "follow : $handle"
echo "atr follow $did"
atr follow $did
fi
if [ "$flb" = "null" ];then
rkey=${flg##*/}
echo followedBy
echo $flg
echo "unfollow : $handle"
echo "atr follow $did -d $rkey"
atr follow $did -d $rkey
fi
done
}
ii=1
while [ "$cursor" != "null" ]
do
page ers $ii
ii=`expr $ii + 1`
done
unset cursor
ii=1
while [ "$cursor" != "null" ]
do
page s $ii
ii=`expr $ii + 1`
done

View File

@@ -1,126 +0,0 @@
[
{"data":"20231003"},
{"data":"20230107"},
{"data":"20230122"},
{"data":"20230206"},
{"data":"20230220"},
{"data":"20230307"},
{"data":"20230322"},
{"data":"20230406"},
{"data":"20230420"},
{"data":"20230506"},
{"data":"20230520"},
{"data":"20230604"},
{"data":"20230618"},
{"data":"20230703"},
{"data":"20230718"},
{"data":"20230802"},
{"data":"20230816"},
{"data":"20230831"},
{"data":"20230915"},
{"data":"20230929"},
{"data":"20231015"},
{"data":"20231029"},
{"data":"20231113"},
{"data":"20231127"},
{"data":"20231213"},
{"data":"20231227"},
{"data":"20240111"},
{"data":"20240126"},
{"data":"20240210"},
{"data":"20240224"},
{"data":"20240310"},
{"data":"20240325"},
{"data":"20240409"},
{"data":"20240424"},
{"data":"20240508"},
{"data":"20240523"},
{"data":"20240606"},
{"data":"20240622"},
{"data":"20240706"},
{"data":"20240721"},
{"data":"20240804"},
{"data":"20240820"},
{"data":"20240903"},
{"data":"20240918"},
{"data":"20241003"},
{"data":"20241017"},
{"data":"20241101"},
{"data":"20241116"},
{"data":"20241201"},
{"data":"20241215"},
{"data":"20241231"},
{"data":"20250114"},
{"data":"20250129"},
{"data":"20250212"},
{"data":"20250228"},
{"data":"20250314"},
{"data":"20250329"},
{"data":"20250413"},
{"data":"20250428"},
{"data":"20250513"},
{"data":"20250527"},
{"data":"20250611"},
{"data":"20250625"},
{"data":"20250711"},
{"data":"20250725"},
{"data":"20250809"},
{"data":"20250823"},
{"data":"20250908"},
{"data":"20250922"},
{"data":"20251007"},
{"data":"20251021"},
{"data":"20251105"},
{"data":"20251120"},
{"data":"20251205"},
{"data":"20251220"},
{"data":"20260103"},
{"data":"20260119"},
{"data":"20260202"},
{"data":"20260217"},
{"data":"20260303"},
{"data":"20260319"},
{"data":"20260402"},
{"data":"20260417"},
{"data":"20260502"},
{"data":"20260517"},
{"data":"20260531"},
{"data":"20260615"},
{"data":"20260630"},
{"data":"20260714"},
{"data":"20260729"},
{"data":"20260813"},
{"data":"20260828"},
{"data":"20260911"},
{"data":"20260927"},
{"data":"20261011"},
{"data":"20261026"},
{"data":"20261109"},
{"data":"20261124"},
{"data":"20261209"},
{"data":"20261224"},
{"data":"20260103"},
{"data":"20260119"},
{"data":"20260202"},
{"data":"20260217"},
{"data":"20260303"},
{"data":"20260319"},
{"data":"20260402"},
{"data":"20260417"},
{"data":"20260502"},
{"data":"20260517"},
{"data":"20260531"},
{"data":"20260615"},
{"data":"20260630"},
{"data":"20260714"},
{"data":"20260729"},
{"data":"20260813"},
{"data":"20260828"},
{"data":"20260911"},
{"data":"20260927"},
{"data":"20261011"},
{"data":"20261026"},
{"data":"20261109"},
{"data":"20261124"},
{"data":"20261209"},
{"data":"20261224"}]

View File

@@ -1,14 +0,0 @@
#!/bin/zsh
admin_password=`cat $HOME/.config/ai/api_card.json|jq -r .pds_admin_password`
if [ -n "$1" ];then
host=$1
else
host=syu.is
fi
url=https://$host/xrpc/com.atproto.server.createInviteCode
json="{\"useCount\":30}"
echo $url
echo $admin_password
#curl -X POST -u admin:${admin_password} -H "Content-Type: application/json" -d "$json" -sL $url | jq -r .code
curl -X POST -u admin:${admin_password} -H "Content-Type: application/json" -d "$json" -sL $url

View File

@@ -1,40 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
host=api.syui.ai
data=`curl -sL "$host/users?itemsPerPage=3000"|jq ".|sort_by(.like)|reverse|.[]|select(.like_rank > 1)"`
tmp=`echo $data|jq -s`
n=`echo $tmp|jq "length"`
ran=$(($RANDOM % n - 1))
echo $ran
function did() {
user=`echo $tmp|jq -r ".[$ran].username"`
did=`echo $tmp|jq -r ".[$ran].did"`
}
function tl(){
did
cid=`$atr f $did|jq -r ".records|.[0].cid"`
uri=`$atr f $did|jq -r ".records|.[0].uri"`
text=`$atr f $did|jq -r ".records|.[0].value.text"`
echo $cid
echo $uri
echo $text
find=`echo $text|grep "card.syui.ai"`
find_t=`echo $text|grep "ten : "`
if [ -n "$find" ] || [ -n "$find_t" ];then
exit
fi
text=`$atr chat "$text" -c`
echo $text
}
function reply(){
tl
if [ -n "$text" ] && [ -n "$uri" ];then
$atr r "$text" -u $uri -c $cid
fi
}
reply

View File

@@ -1,390 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
PATH=$PATH:$HOME/.cargo/bin
d=$HOME/.config/msr
f=$HOME/.config/msr/notify_log.txt
card=$HOME/.config/ai/scpt/api_card.zsh
if [ ! -d $d ];then
mkdir -p $d
fi
j=`$HOME/.cargo/bin/msr bot`
tmp=`echo $j|jq length`
if [ $tmp -eq 0 ];then
exit
fi
data_id=`echo $j|jq -r ".id"`
n=`echo $data_id|wc -l`
data_mid=`echo $j|jq -r ".mid"`
data_text=`echo $j|jq -r ".body"`
data_url=`echo $j|jq -r ".url"`
data_server=`echo $j|jq -r ".url"|cut -d / -f 3`
data_user=`echo $j|jq -r ".user"`
function user_create() {
if [ -n "$ap" ] && [ -n "$url" ];then
data=`curl -X POST -H "Content-Type: application/json" -d "{\"username\":\"$ap\",\"password\":\"$pass\",\"did\":\"$url\",\"next\":\"$nd_o\",\"updated_at\":\"$updated_at_o\"}" -sL "$host/users"`
uid=`echo $data|jq -r ".id"`
echo id $uid
else
echo error user create
fi
}
function card_env() {
card_url=https://card.syui.ai
host=https://api.syui.ai
d=`date +"%Y%m%d"`
nd=`date +"%Y%m%d" -d '1 day'`
nd_o=`date +"%Y%m%d" -d '-1 day'`
updated_at_o=`date --iso-8601=seconds -d '-1 day'`
username=`echo $1|cut -d . -f 1`
url_user_all="$host/users?itemsPerPage=2000"
pass=`cat $HOME/.config/ai/api_card.json|jq -r .password`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
data_tmp=`curl -sL $url_user_all`
data=`echo "$data_tmp"|jq ".[]|select(.username == \"$ap\")"`
if [ -z "$data" ];then
data=`echo "$data_tmp"|jq ".[]|select(.username == \"$username\")"`
fi
if [ -z "$data" ];then
echo no $username
user_create
fi
username=`echo $data|jq -r .username`
data_did_check=`echo $data|jq -r .did`
uid=`echo $data|jq -r ".id"`
delete=`echo $data|jq -r ".delete"`
mastodon=`echo $data|jq -r ".mastodon"`
did=`echo $data|jq -r ".did"`
handle_change=`echo $data|jq -r ".handle"`
raid_at=`echo $data|jq -r .raid_at`
raid_at=`date -d "$raid_at" +"%Y%m%d"`
raid_at_n=`date --iso-8601=seconds`
server_at=`echo $data|jq -r .server_at`
server_at=`date -d "$server_at" +"%Y%m%d"`
server_at_n=`date --iso-8601=seconds`
updated_at=`echo $data|jq -r .updated_at`
updated_at=`date -d "$updated_at" +"%Y%m%d"`
updated_at_n=`date --iso-8601=seconds`
next=`echo $data|jq -r .next`
aiten=`echo $data|jq -r .aiten`
ten_su=`echo $data|jq -r .ten_su`
if [ "$a_team" = false ];then
echo "bsky @${username}"
echo "no activitypub-mode"
return 0
fi
echo "$card_url/$username"
}
function card_day() {
card_env $1
if [ $next -gt $d ] || [ "$updated_at" = "$d" ];then
echo limit 1 day
return 0
fi
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"password\":\"$pass\"}" -sL $host/cards`
card=`echo $tmp|jq -r .card`
card_url=`echo $tmp|jq -r .url`
cp=`echo $tmp|jq -r .cp`
skill=`echo $tmp|jq -r .skill`
if [ -z "$card" ];then
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"password\":\"$pass\"}" -sL $host/cards`
card=`echo $tmp|jq -r .card`
card_url=`echo $tmp|jq -r .url`
cp=`echo $tmp|jq -r .cp`
skill=`echo $tmp|jq -r .skill`
fi
echo "[card]"
echo id : $card
echo cp : $cp
if [ "$skill" != "normal" ];then
echo skill : $skill
fi
t=`echo $tmp|jq -r .card`
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"next\":\"$nd\",\"token\":\"$token\"}" -sL $host/users/$uid`
}
function card_b() {
card_env $1
if [ $updated_at -ge $d ] || [ "$updated_at" = "$d" ];then
echo "limit battle"
return 0
fi
id_all=`curl -sL "$host/users?itemsPerPage=2000"|jq ".[]|.id"`
id_n=`echo "$id_all"|wc -l`
id_nr=$(($RANDOM % $id_n))
r=`echo "$id_all"| awk "NR==$id_nr"`
if [ "$id_all" = "null" ];then
r=2
fi
if [ 0 -eq $id_n ] || [ 0 -eq $r ];then
r=2
fi
if [ -z "$id_n" ] || [ -z "$r" ];then
r=2
fi
data_uu=`curl -sL "$host/users/$uid/card?itemsPerPage=2000"`
data_u=`curl -sL "$host/users/$r/card?itemsPerPage=2000"`
tt=`echo $data_uu|jq ".[].cp"|sort -n -r`
ttt=`echo $data_u|jq ".[].cp"|sort -n -r`
#echo $data_u|jq ".[].cp"
nl=`echo $data_uu|jq length`
if [ $nl -ge 3 ];then
rs=$(($RANDOM % 3 + 1))
else
rs=$(($RANDOM % $nl + 1))
fi
#echo $data_u|jq ".[].cp"
nll=`echo $data_u|jq length`
rss=$(($RANDOM % $nll))
if [ $nll -ge 3 ];then
rss=$(($RANDOM % 3 + 1))
else
rss=$(($RANDOM % $nll + 1))
fi
cp_i=`echo $tt |awk "NR==$rs"`
cp_b=`echo $ttt |awk "NR==$rss"`
if [ -z "$cp_i" ];then
echo "null error"
return 0
fi
if [ -z "$cp_b" ];then
echo "null error"
return 0
fi
echo $tt | sed -n 1,3p
echo "---"
echo id : $r
echo $ttt | sed -n 1,3p
echo "---"
echo $cp_i vs $cp_b
if [ $cp_i -gt $cp_b ];then
echo "win!"
else
echo loss
fi
if [ $cp_i -gt $cp_b ];then
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"password\":\"$pass\"}" -sL $host/cards`
card=`echo $tmp|jq -r .card`
card_url=`echo $tmp|jq -r .url`
cp=`echo $tmp|jq -r .cp`
if [ -z "$card" ];then
tmp=`curl -X POST -H "Content-Type: application/json" -d "{\"owner\":$uid,\"password\":\"$pass\"}" -sL $host/cards`
card=`echo $tmp|jq -r .card`
card_url=`echo $tmp|jq -r .url`
cp=`echo $tmp|jq -r .cp`
fi
echo "[card]"
echo id : $card
echo cp : $cp
t=`echo $tmp|jq -r .card`
fi
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"updated_at\":\"$updated_at_n\",\"token\":\"$token\"}" -sL $host/users/$uid`
}
function card_s(){
card_env $1
username=$1
a_team=mastodon
b_team=bluesky
rr=`date +"%H%M"`
f_server=$HOME/.config/ai/txt/card_server.txt
f_server_user_at=$HOME/.config/ai/txt/card_server_user_at.txt
f_server_user_ap=$HOME/.config/ai/txt/card_server_user_ap.txt
f_server_ap=$HOME/.config/ai/txt/card_server_ap.txt
f_server_at=$HOME/.config/ai/txt/card_server_at.txt
f_server_start_time=$HOME/.config/ai/txt/card_server_start_time.txt
if [ `cat $f_server` -eq 1 ];then
echo shutdown server battle
exit
fi
if [ ! -f $f_server_start_time ];then
server_start=`date +"%H%M"`
echo "$server_start" >! $f_server_start_time
echo 0 >! $f_server_at
echo 0 >! $f_server_ap
fi
cp_ap=`cat $f_server_ap`
cp_at=`cat $f_server_at`
if [ -f $f_server_start_time ];then
server_start=`cat $f_server_start_time`
server_time=`date -d "$server_start 30 min" +"%H%M"`
fi
#echo "time:`date -d "$server_time" +"%H:%M"`"
if [ $server_at -ge $d ] || [ "$server_at" = "$d" ];then
echo "limit battle"
exit
fi
data_uu=`curl -sL "$host/users/$uid/card?itemsPerPage=2000"`
fav_card=`echo $data_uu|jq "sort_by(.cp)|reverse|.[0]"`
if [ ! -f $f_server_user_at ];then
echo start >> $f_server_user_at
fi
if [ ! -f $f_server_user_ap ];then
echo start >> $f_server_user_ap
fi
commit_user_at=`cat $f_server_user_at|tail -n 1`
commit_user_ap=`cat $f_server_user_ap|tail -n 1`
echo $username >> $f_server_user_ap
cp_i=`echo $fav_card|jq -r ".cp"`
cp_ii=$cp_i
card_name=`echo $fav_card|jq -r ".card"`
card_status=`echo $fav_card|jq -r ".status"`
card_skill=`echo $fav_card|jq -r ".skill"`
skill=$card_skill
if [ "$skill" = "critical" ];then
cp_i=$((cp_i + cp_i))
fi
if [ "$skill" = "dragon" ];then
cp_i=$((cp_i * 3))
fi
if [ "$skill" = "yui" ];then
cp_i=$((cp_i + ten_su))
fi
cp_all=$((cp_i + cp_ap))
# サーバーバトルの還元期間
#cp_all=$((cp_ap - cp_i))
if [ "$skill" = "critical" ];then
echo "$cp_i ---> $cp_ap"
elif [ "$skill" = "post" ];then
cp_post=`$HOME/.cargo/bin/atr pro $1 -p`
cp_i=$((cp_i + cp_post))
cp_all=$((cp_i + cp_ap))
echo "🔥 $cp_i ---> $cp_ap"
elif [ "$skill" = "luck" ];then
echo "$cp_i ---> $cp_ap"
elif [ "$skill" = "dragon" ];then
echo "🐉 $cp_i ---> $cp_ap"
elif [ "$skill" = "yui" ];then
echo "🔅 $cp_i ---> $cp_ap"
else
echo "$cp_i ---> $cp_ap"
fi
echo $cp_all >! $f_server_ap
echo
echo "[${a_team}] ${cp_all}"
echo "┣ @${username}"
echo "┗ @${commit_user_ap}"
echo
echo "┏━ vs ━┛"
echo
echo "[${b_team}] ${cp_at}"
echo "┗ @${commit_user_at}"
#echo "[log]"
#echo "${cp_ap}/$a_team --> ${commit_user_ap}"
#echo "${commit_user_at} <-- ${cp_at}/$b_team"
#echo "${username} --> $cp_all/$a_team"
if [ $rr -gt $server_time ];then
#echo "----"
#echo "timeup!"
echo 1 >! $f_server
rm $f_server_start_time
rm $f_server_at
rm $f_server_ap
mv $f_server_user_at $f_server_user_at.back
mv $f_server_user_ap $f_server_user_ap.back
fi
echo "----"
cp_plus=$(($RANDOM % 100 + 1))
cp=$((cp_ii + cp_plus))
body="level up!"
echo "${body}${cp}(+${cp_plus})"
tmp=`curl -sL -X PATCH -H "Content-Type: application/json" -d "{\"cp\":$cp,\"token\":\"$token\"}" $host/cards/$fav`
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"server_at\":\"$server_at_n\", \"token\":\"$token\"}" -s $host/users/$uid`
}
function mastodon_notify() {
for ((i=1;i<=$n;i++))
do
mid=`echo $data_mid|awk "NR==$i"`
check=`cat $f|grep $mid`
if [ -n "$check" ];then
echo ok
continue
fi
url=`echo $data_url|awk "NR==$i"`
user=`echo $data_user|awk "NR==$i"`
text=`echo $data_text|awk "NR==$i"`
server=`echo $data_server|awk "NR==$i"`
ap="@${user}@${server}"
if [ -f "$GOPATH/bin/pup" ];then
text=`echo ${text}|pup "p text{}"|tail -n 1|cut -d " " -f 2-|tr -d '"'|sed -e "s/&#39;/'/g" -e 's/&quot;/"/g'`
else
text=`echo ${text}|sed -e 's/<[^>]*>//g'|cut -d " " -f 2-|tr -d '"'|sed -e "s/&#39;/'/g" -e 's/&quot;/"/g'`
fi
echo $text
com=`echo $text|cut -d " " -f 1`
opt=`echo $text|cut -d " " -f 2`
if [ "card" = "$com" ] || [ "/card" = "$com" ];then
if [ "b" = "$opt" ] || [ "-b" = "$opt" ];then
text=`card_b $user`
echo $user $text
msr cn "@${user}@${server} `echo $text`" -mm $mid
echo $mid >> $f
continue
fi
if [ "s" = "$opt" ] || [ "-s" = "$opt" ];then
text=`card_s $user`
echo $user $text
msr cn "@${user}@${server} `echo $text`" -mm $mid
echo $mid >> $f
continue
fi
text=`card_day $user`
echo $user $text
msr cn "@${user}@${server} `echo $text`" -mm $mid
echo $mid >> $f
fi
done
}
mastodon_notify

View File

@@ -1,107 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
atr=$HOME/.cargo/bin/atr
host=https://api.syui.ai
host_card=https://card.syui.ai/json/card.json
pass=`cat $HOME/.config/atr/api_card.json|jq -r .password`
token=`cat $HOME/.config/atr/api_card.json|jq -r .token`
handle=$1
username=`echo $1|cut -d . -f 1`
did=$2
cid=$3
uri=$4
opt=$5
pay=100
eat_file=$HOME/.config/atr/txt/nyancat_eat.txt
ran=`echo $(($RANDOM % 15))`
eat="🍺☕ 🍵 🍶 🍼🍻 🍸 🍹 🍷 🍴 🍕 🍔 🍟 🍗 🍖 🍝🍛 🍤 🍱 🍣 🍥 🍙 🍘 🍚 🍜 🍲 🍢🍡 🍳 🍞 🍩 🍮 🍦 🍨 🍧 🎂 🍰 🍪🍫 🍬 🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉🍓 🍑 🍈 🍌 🍐 🍍 🍠 🍆 🍅 🌽"
eat=`echo $eat|grep "$opt"`
if [ -z "$eat" ];then
ran=`echo $(($RANDOM % 3))`
if [ $ran -eq 1 ];then
body="| キュー"
elif [ $ran -eq 2 ];then
body="| ミュー"
else
body="| ピャー"
fi
echo "uncooked"
echo "A__A
|・ㅅ・ |
|っ c|
$body
U ̄ ̄U"
exit
fi
echo "| ${opt} |" >> $eat_file
body_d="A__A
|・ㅅ・ |
|っ c|
`tac $eat_file|grep -v '| |'`
U ̄ ̄U"
function card_user(){
all_data=`curl -sL "$host/users?itemsPerPage=3000"`
data=`echo $all_data|jq ".[]|select(.username == \"$username\")"`
uid=`echo $data|jq -r .id`
aiten=`echo $data|jq -r .aiten`
like=`echo $data|jq -r .like`
ten_data=`echo $all_data|jq ".|sort_by(.aiten)|reverse|.[]|select(.aiten >= $pay)"`
if [ -z "$ten_data" ] || [ -z "$aiten" ] || [ $aiten -le $pay ];then
echo "aiten : $aiten >= $pay"
echo "failed to buy food"
echo "please : @yui.syui.ai /ten"
exit
else
pay_s=$((aiten - pay))
like_s=$((like + 1))
if [ $pay_s -le 0 ] || [ -z "$pay_s" ];then
echo "aiten : $aiten >= $pay"
echo "failed to buy food"
echo "please : @yui.syui.ai /ten"
exit
fi
body_user=`echo "${aiten} : $aiten - $pay = $pay_s"`
fi
}
function card_like() {
if [ $like -gt 3 ] && [ $ran -eq 1 ];then
echo "happy!"
su=`cat $eat_file|wc -l`
su=$((su * 50))
pay_s=$((aiten + su))
body_user=`echo "${aiten} : $aiten + $su = $pay_s"`
rm $eat_file
echo "| ${opt} |" >> $eat_file
body_d="A__A
|・ㅅ・ |
|っ c|
`tac $eat_file|grep -v '| |'`
U ̄ ̄U"
fi
}
function card_pay(){
link=https://card.syui.ai/$username
text=`echo "$body_user\n$body_d"`
desc="[$ten]"
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"like\":$like_s,\"token\":\"$token\", \"aiten\": $pay_s}" -s $host/users/$uid`
echo "$text"
}
card_user
card_like
card_pay

View File

@@ -1,36 +0,0 @@
#!/bin/zsh
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
atr=$HOME/.cargo/bin/atr
host_card=https://card.syui.ai/json/card.json
pass=`cat $HOME/.config/atr/api_card.json|jq -r .password`
token=`cat $HOME/.config/atr/api_card.json|jq -r .token`
eat_file=$HOME/.config/atr/txt/nyancat_eat.txt
eat=`cat $eat_file|awk "NR==1"`
if [ -z "$eat" ];then
exit
fi
body_d="thx!
A__A
|・ㅅ・ |
|っ c|
`cat $eat_file|awk "NR==1"`
U ̄ ̄U"
host=https://api.syui.ai
data=`curl -sL "$host/users?itemsPerPage=3000"|jq ".[]|select(.like >= 10)"`
tmp=`echo $data|jq -s`
n=`echo $tmp|jq "length"`
ran=$(($RANDOM % n - 1))
echo $ran
user=`echo $tmp|jq -r ".[$ran].username"`
did=`echo $tmp|jq -r ".[$ran].did"`
echo $atr @ $did -p "`echo $body_d`"
$atr @ $did -p "`echo $body_d`"

View File

@@ -1,49 +0,0 @@
#!/bin/zsh
handle=$1
did=$2
text=$3
atr=$HOME/.cargo/bin/atr
url_j=https://card.syui.ai/json/card.json
handle=$1
did=$2
cid=$3
uri=$4
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
url=https://api.syui.ai
username=`echo $1|cut -d . -f 1`
link=https://card.syui.ai/$username
uid=`curl -sL "$url/users?itemsPerPage=2000"|jq ".[]|select(.username == \"$username\")"|jq -r .id`
echo $uid
data=`curl -sL "$url/users/$uid"`
like_old=`echo $data|jq -r .like`
nolike=$(($RANDOM % 30))
like=$(($RANDOM % 10))
like_at=`date --iso-8601=seconds`
token=`cat $HOME/.config/ai/api_card.json|jq -r .token`
echo nolike $nolike
echo like $like
#if [ $like_old -eq 100 ];then
# $atr follow $did
# like=$((1 + like_old))
# curl -X PATCH -H "Content-Type: application/json" -d "{\"token\":\"$token\", \"like\": $like}" -s $url/users/$uid
#fi
if { [ $like -eq 1 ] && echo $text|grep -e "ありがとう" -e "うれしい" } || [ $nolike -eq 1 ];then
echo ok
#$atr @ $handle -p "♡"
like=$((1 + like_old))
curl -X PATCH -H "Content-Type: application/json" -d "{\"token\":\"$token\", \"like\": $like}" -s $url/users/$uid
fi
exit

View File

@@ -1,70 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
url=https://api.syui.ai
token=`cat $HOME/.config/atr/api_card.json|jq -r .token`
if [ "$2" = "reset" ];then
echo reset : $1
username=`echo $1|cut -d . -f 1`
uid=`curl -sL "$url/users?itemsPerPage=3000"|jq ".[]|select(.username == \"$username\")"|jq -r .id`
like=0
tmp=`curl -X PATCH -H "Content-Type: application/json" -d "{\"like\":\"$like\", \"token\":\"$token\"}" -s $url/users/$uid`
exit
fi
s=$((RANDOM % 5))
json=`curl -sL "https://api.syui.ai/users?itemsPerPage=3000"|jq "sort_by(.like)|reverse|.[$s]"`
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
username=`echo $json|jq -r .username`
if [ "$username" = "ai" ] || [ "$username" = "yui" ];then
exit
fi
did=`echo $json|jq -r .did`
link=https://card.syui.ai/$username
uid=`echo $json|jq -r .id`
data=`curl -sL "$url/users/$uid"`
like_old=`echo $data|jq -r .like`
like_rank=`echo $json|jq -r .like_rank`
like_rank_new=$((like_rank + 1))
echo $data
#test
if [ "$1" = "-t" ];then
echo $json
did=did:plc:uqzpqmrjnptsxezjx4xuh2mn
like_old=6
fi
if [ $like_old -ge 100 ] && [ $like_rank -eq 2 ];then
text=`$atr chat "相手に好きな気持を伝えてください" -c|sed '/^$/d'`
$atr @ $did -p "$text"
curl -X PATCH -H "Content-Type: application/json" -d "{\"like_rank\":$like_rank_new, \"token\":\"$token\"}" -s $url/users/$uid
exit
fi
if [ $like_old -ge 10 ] && [ $like_rank -eq 1 ];then
text=`$atr chat "相手を心配してください" -c|sed '/^$/d'`
$atr @ $did -p "$text"
curl -X PATCH -H "Content-Type: application/json" -d "{\"like_rank\":$like_rank_new, \"token\":\"$token\"}" -s $url/users/$uid
exit
fi
if [ $like_old -ge 5 ] && [ $like_rank -eq 0 ];then
text=`$atr chat "自己紹介してください" -c|sed '/^$/d'`
$atr @ $did -p "$text"
curl -X PATCH -H "Content-Type: application/json" -d "{\"like_rank\":$like_rank_new, \"token\":\"$token\"}" -s $url/users/$uid
exit
fi
exit

View File

@@ -1,46 +0,0 @@
#!/bin/zsh
atr=$HOME/.cargo/bin/atr
dir=${0:a:h:h}
OPENAI_API_KEY=`cat ~/.config/atr/openai.toml |cut -d "'" -f 2`
url=https://api.openai.com/v1/chat/completions
cfg=`echo "あなたはyuiという作品のアイと呼ばれるキャラクターです。アイの口調で回答してください。第一人称は「アイ」です。\
第二人称はできる限り省略してください。\
アイは他人に謝ることはありません。自由気ままな優しさを兼ね備えます。\
アイのよく使う口癖は次のとおりです。その口癖に合わせた感じで話してください。\
......。\
アイね、ここでお昼寝するの。\
うん。\
そうだね。\
面白いね。\
楽しいね。\
どうしたの?
"`
json="{
\"model\": \"gpt-3.5-turbo\",
\"messages\": [
{\"role\": \"system\", \"content\": \"$cfg\"},
{\"role\": \"user\", \"content\": \"$q\"}
]
}"
function openai_api(){
curl -sL $url \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "$json"
}
if [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ];then
q=${@:3:($#-2)}
cid=$1
uri=$2
else
echo no query
exit
fi
content=`openai_api "$q" | jq -r ".choices|.[]|.message.content"`
$atr r "$content" -c $cid -u $uri

View File

@@ -1,107 +0,0 @@
#!/bin/zsh
url_plc="https://plc.directory/export"
host_at=bsky.social
url=https://plc.directory
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
dir=$HOME/.config/ai/txt
file=$dir/user_list.txt
dir_git_card_page=$HOME/git/card.syui.ai
if [ ! -d $dir_git_card_page ];then
mkdir -p $HOME/git
cd $HOME/git
git clone https://github.com/syui/card.syui.ai
else
cd $dir_git_card_page
t=`git pull`
fi
file_photo=$dir_git_card_page/public/json/photo.json
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
created_at=`date --iso-8601=seconds`
#if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ];then
# exit
#fi
function fan_art_search() {
k="aiphoto"
url="search.bsky.social/search/posts?q="
t=`curl -sL "${url}${k}"|jq ".[]|select(.post.text == \"$k\")"`
#n=`curl -sL "${url}${k}"|jq length`
n=$((n - 1))
for ((i=0;i<=$n;i++))
do
did=`curl -sL "${url}${k}"|jq -r ".[$i]|.user.did"`
handle=`curl -sL https://plc.directory/$did|jq -r ".alsoKnownAs|.[]"|cut -d / -f 3-`
if [ -z "$handle" ];then
continue
fi
tid=`curl -sL "${url}${k}"|jq -r ".[$i]|.tid"|cut -d / -f 2`
http=https://staging.bsky.app/profile/$handle/post/$tid
echo $http
done
}
if [ "$1" = "-s" ];then
fan_art_search
exit
fi
if ! echo $1|grep "." >/dev/null 2>&1;then
echo "ex : user syui.bsky.social"
exit
fi
if ! echo $2|grep "did:plc:" >/dev/null 2>&1;then
echo "ex : user did"
exit
fi
if [ "$3" = "-l" ];then
curl -sL card.syui.ai/json/photo.json|jq -r ".[]|.author, .link"
exit
fi
if ! echo $3|grep "bsky.app/profile/">/dev/null 2>&1;then
echo "please url : bsky.app/profile/$1/post/xxx"
exit
fi
if ! echo $4|grep "av-cdn.bsky.app/img/">/dev/null 2>&1;then
echo "please url : av-cdn.bsky.app/img"
exit
fi
function fan_art(){
add=$1
did=$2
link=$3
img=`echo $4|tr -d "'"`
author=`echo $3|cut -d / -f 5`
cd $dir_git_card_page
check_null=`cat $file_photo|jq ".[]|select(.img == \"$img\")"`
if [ -n "$check_null" ];then
echo registered
exit
fi
echo `cat $file_photo` "[{\"add\":\"$add\",\"link\":\"$link\",\"author\":\"$author\",\"img\":\"$img\",\"created_at\":\"$created_at\",\"did\":\"$did\"}]" | jq -s add >! $file_photo.back
if cat $file_photo.back|jq . >/dev/null 2>&1;then
mv $file_photo.back $file_photo
git add $file_photo
git commit -m "add photo"
git push -u origin main -f
echo add photo, thx $1
echo "author : $author"
echo "it will take some time to deploy"
fi
}
fan_art $1 $2 $3 $4
exit

View File

@@ -1,52 +0,0 @@
[
{
"tag": "girl",
"model":"model",
"body": "masterpiece, best quality, 1girl, solo, flower, long hair, outdoors, letterboxed, school uniform, day, sky, looking up, short sleeves, parted lips, shirt, cloud, black hair, sunlight, white shirt, serafuku, upper body, from side, pink flower, blurry, brown hair, blue sky, depth of field"
},
{
"tag": "girl",
"model":"model",
"body": "best quality, high quality photo, clear face, detailed face, beautiful face, photo rialistic, balcony, baroque architecture, 1girl, 16yo, bronde, very long hair, windy, floating hair, see through frilled white dress, look at outside, white lily, starry night, big moon, moon light, volumetric lighting, side shot, , good anatomy, perspective"
},
{
"tag": "gril",
"model":"model",
"body": "mksks style, masterpiece, ultra-detailed, illustration,best quality, blue sky, ((cloudy sky)), rooftop, cityscape, hatsune miku, yuki miku, yuki miku (2017), yuki miku (2019), perfect lighting, science fiction, (looking at viewer), ((kawaii)), loli, fantastic_eyes, hyper extreme detailed, (glowing_eyes:0.95), highly detailed symmetric faces, holding bouque"
},
{
"tag": "gril",
"model":"model",
"body": "1girl, 12yo,(room animal costume:1.2),baby (chick:1.2) costume, fluffy, baby chick concept room, fancy, cute, costume, A small cute chick wrapped in pale yellow feathers. Its round body and soft feathers seem to celebrate the miracle of life. The sparkle of life. Cute orange beak,pastel color <lora:luckyStarStyleLora_offset:0.8>"
},
{
"tag": "gril",
"model": "model",
"body": "masterpiece, best quality, very_high_resolution, large_filesize, full color, beautiful kawaii, gold hair, little girl"
},
{
"tag": "field",
"model":"pvcstyle",
"body": "picture by lvl landscape highly detailed high detailed concept art HD art HQ illustration 4k illustration 8k illustration 4K wallpaper 8K wallpaper Highly Cinematic Lighting cinematic Beautiful starry sky milky way extreme high detailed colorful flower garden from above"
},
{
"tag": "field",
"model":"model",
"body": "flying warship, no humans, letterboxed, spacecraft, cloud, sky, turret <lora:flying warship-noise:0.7>"
},
{
"tag": "gril",
"model": "model",
"body": "traininganyahyper-1800, 1girl, ahoge, animal_ears, aqua_eyes, bangs, black_hairband, blurry, blurry_background, blush, bokeh, bow, bush, cat_ears, christmas_tree, clenched_hand, clenched_hands, collarbone, day, depth_of_field, diffraction_spikes, dress, eyebrows_visible_through_hair, fake_animal_ears, forest, grass, green_eyes, hairband, hands_up, lens_flare, long_sleeves, looking_at_viewer, nature, open_mouth, outdoors, park, path, paw_pose, photo_background, solo, sparkle, sparkling_eyes, sunlight, tree<hypernet:traininganyahyper-1800:1.0>"
},
{
"tag": "gril",
"model": "model",
"body": "(best quality),(masterpiece),(high resolution),(silky skin), (upper body:1.9), smile, blush, looking at viewer, detailed angel wing, detailed angel halo, wedding, veil, (girl extend one hand and show the wedding ring), standing, (1girl), (child), (little), (thin), (small breasts:1.5), (blond middle hair, cutoff), (extremely beautiful eyes), in charch, perfect anatomy, unity 8k wallpaper, <lora:toddler-xl-v2_last:-2>"
},
{
"tag": "gril",
"model": "model",
"body": "masterpiece, best quality, (photography:1.3), (high quality:1.2), detailed face, detailed eyes, A girl in a white one-piece dress standing on the sidewalk of a rice field at dusk gazes dreamily at the crimson sky reflected on the surface of the water"
}
]

View File

@@ -1,20 +0,0 @@
#!/bin/zsh
f=`grep -R '$HOME/.config/atr' . |cut -d : -f 1|sort|uniq|grep -v t.zsh`
echo $f
n=`echo $f|wc -l`
for ((i=1;i<=$n;i++))
do
ff=`echo $f|awk "NR==$i"`
echo $i
echo $ff
sed 's#$HOME/.config/atr#$HOME/.config/ai#g' $ff|grep '$HOME/.config'
case $OSTYPE in
linux*)
sed -i 's#$HOME/.config/atr#$HOME/.config/ai#g' $ff
;;
darwin*)
sed -i "" 's#$HOME/.config/atr#$HOME/.config/ai#g' $ff
;;
esac
done

View File

@@ -1,56 +0,0 @@
#!/bin/zsh
#
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
if [ "$1" = "test" ] || [ -z "$1" ];then
handle=syui.ai
else
handle=$1
fi
post=0
d=`date +"%Y-%m-%d"`
od=`date +"%Y-%m-%d" --date '1 day ago'`
unset cursor
function first_record(){
cursor=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post&limit=100" |jq -r ".cursor"`
t=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post&limit=100" |jq -r ".[]|.[]?|.value.createdAt"|cut -d T -f 1`
n=`echo $t|wc -l`
}
function cursor_record(){
cursor=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post&limit=100&cursor=$cursor" |jq -r ".cursor"`
t=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post&limit=100&cursor=$cursor" |jq -r ".[]|.[]?|.value.createdAt"|cut -d T -f 1`
n=`echo $t|wc -l`
}
function day_check(){
for ((i=1;i<=$n;i++))
do
tt=`echo $t|awk "NR==$i"`
if [ "$tt" = "$d" ];then
post=$((post + 1))
echo $post
fi
if [ "$tt" = "$od" ];then
echo $tt $od
echo $post
exit
fi
done
}
for ((ii=1;ii<=100;ii++))
do
if [ $ii -eq 1 ];then
first_record
else
echo $cursor
cursor_record
fi
day_check
done

View File

@@ -1,138 +0,0 @@
#!/bin/zsh
url_plc="https://plc.directory/export"
host_at=bsky.social
url=https://plc.directory
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
dir=$HOME/.config/atr/txt
file=$dir/user_list.txt
atr=$HOME/.cargo/bin/atr
unset timed
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
if [ -z "$1" ];then
exit
fi
if ! echo $1|grep "." >/dev/null 2>&1;then
echo "ex : user syui.bsky.social"
exit
fi
if [ ! -d $dir ];then
mkdir -p $dir
fi
if [ ! -f $file ];then
touch $file
fi
function mfile() {
t=`cat $file|sort|uniq`
if [ -n "$t" ];then
echo "$t" >! ${file}.back
mv ${file}.back $file
fi
}
function plc(){
if cat $file|grep "$1" >/dev/null 2>&1;then
cat $file|grep "$1"
exit
fi
json_tmp=`curl -sL "${url_plc}?after=${timed}"|jq .`
json=`echo $json_tmp|jq "select(.operation.handle == \"$1\")"`
if [ -z "$json" ];then
check=`echo $json_tmp|jq -r ".operation.alsoKnownAs"|head -n 1`
if [ "null" != "$check" ];then
json=`echo $json_tmp|jq "select(.operation.alsoKnownAs|.[] == \"at://$1\")"` >/dev/null 2>&1
fi
fi
if [ -n "$json" ];then
created_at=`echo $json|jq -r .createdAt |tail -n 1`
fi
if [ -n "$created_at" ];then
echo "$created_at : $1"
echo "$created_at : $1" >> $file
mfile
exit
fi
}
function fan_art(){
if ! echo $3|grep "https://bsky.app/profile/">/dev/null 2>&1;then
echo "please url : https://bsky.app/profile/$1/post/xxx"
exit
fi
if [ -z "$4" ];then
echo "please img-url : https://example.com/img.png"
exit
fi
img=$4
author=`echo $3|cut -d / -f 5`
cd $dir_git_card_page
cat $file_fanart|jq ".+ {\"add\":\"$1\",\"link\":\"$3\",\"author\":\"$author\",\"img\":\"$img\"}" >! $file_fanart.back
if cat $file_fanart|jq . ;then
mv $file_fanart.back $file_fanart
git add $file_fanart
git commit -m "add fanart"
git push -u orgin main
fi
}
if [ "$2" = "--url" ];then
if [ -z "$3" ];then
exit
fi
fan_art $3
exit
fi
function first(){
#https://bsky.app/profile/$1/post/$e
handle=$2
did="$atr did $2"
curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$handle&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.uri,.value.text,.value.createdAt"
}
if [ "$2" = "-f" ] || [ "$2" = "f" ];then
first $1
exit
fi
if [ "$2" = "-l" ];then
mfile
cat $file
exit
fi
function first_created(){
#https://bsky.app/profile/$1/post/$e
#curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$1&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.createdAt"
curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$1&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.value.createdAt"
}
if [ -z "$2" ];then
first_created $1
fi
if [ -n "$2" ] ;then
first $2
fi
#for ((i=0;i<=300;i++))
#do
# if [ $i -eq 0 ];then
# timed="1970-01-01"
# fi
# plc $1
# timed=`echo $json_tmp|jq -r .createdAt|tail -n 1`
#done

View File

@@ -1,129 +0,0 @@
#!/bin/zsh
url_plc="https://plc.directory/export"
host_at=bsky.social
url=https://plc.directory
url_at=https://$host_at/xrpc/com.atproto.repo.listRecords
dir=$HOME/.config/ai/txt
file=$dir/user_list.txt
unset timed
case $OSTYPE in
darwin*)
alias date="/opt/homebrew/bin/gdate"
;;
esac
if [ -z "$1" ];then
exit
fi
if ! echo $1|grep "." >/dev/null 2>&1;then
echo "ex : user syui.bsky.social"
exit
fi
if [ ! -d $dir ];then
mkdir -p $dir
fi
if [ ! -f $file ];then
touch $file
fi
function mfile() {
t=`cat $file|sort|uniq`
if [ -n "$t" ];then
echo "$t" >! ${file}.back
mv ${file}.back $file
fi
}
function plc(){
if cat $file|grep "$1" >/dev/null 2>&1;then
cat $file|grep "$1"
exit
fi
json_tmp=`curl -sL "${url_plc}?after=${timed}"|jq .`
json=`echo $json_tmp|jq "select(.operation.handle == \"$1\")"`
if [ -z "$json" ];then
check=`echo $json_tmp|jq -r ".operation.alsoKnownAs"|head -n 1`
if [ "null" != "$check" ];then
json=`echo $json_tmp|jq "select(.operation.alsoKnownAs|.[] == \"at://$1\")"` >/dev/null 2>&1
fi
fi
if [ -n "$json" ];then
created_at=`echo $json|jq -r .createdAt |tail -n 1`
fi
if [ -n "$created_at" ];then
echo "$created_at : $1"
echo "$created_at : $1" >> $file
mfile
exit
fi
}
function fan_art(){
if ! echo $3|grep "https://bsky.app/profile/">/dev/null 2>&1;then
echo "please url : https://bsky.app/profile/$1/post/xxx"
exit
fi
if [ -z "$4" ];then
echo "please img-url : https://example.com/img.png"
exit
fi
img=$4
author=`echo $3|cut -d / -f 5`
cd $dir_git_card_page
cat $file_fanart|jq ".+ {\"add\":\"$1\",\"link\":\"$3\",\"author\":\"$author\",\"img\":\"$img\"}" >! $file_fanart.back
if cat $file_fanart|jq . ;then
mv $file_fanart.back $file_fanart
git add $file_fanart
git commit -m "add fanart"
git push -u orgin main
fi
}
if [ "$2" = "--url" ];then
if [ -z "$3" ];then
exit
fi
fan_art $3
exit
fi
function first(){
#https://bsky.app/profile/$1/post/$e
curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$1&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.uri,.value"
}
if [ "$2" = "-f" ];then
first $1
exit
fi
if [ "$2" = "-l" ];then
mfile
cat $file
exit
fi
function first_created(){
#https://bsky.app/profile/$1/post/$e
#curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$1&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.createdAt"
curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$1&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.value.createdAt"
}
first_created $1
#for ((i=0;i<=300;i++))
#do
# if [ $i -eq 0 ];then
# timed="1970-01-01"
# fi
# plc $1
# timed=`echo $json_tmp|jq -r .createdAt|tail -n 1`
#done

View File

@@ -1,51 +0,0 @@
#!/bin/zsh
if [ -n "$1" ];then
did=$1
else
did=`atr did yui.syui.ai`
fi
pds=`curl -sL https://plc.directory/$did|jq -r ".service.[].serviceEndpoint" | cut -d / -f 3-`
handle=`curl -sL https://plc.directory/$did|jq -r ".alsoKnownAs.[]"|cut -d / -f 3-`
old_pds=`curl -sL https://plc.directory/$did/log|jq -r ".[0].service"|cut -d / -f 3-`
old_handle=`curl -sL https://plc.directory/$did/log|jq -r ".[0]|.handle"`
first_post=`curl -sL "https://bsky.social/xrpc/com.atproto.repo.listRecords?repo=$did&collection=app.bsky.feed.post&reverse=true" |jq -r ".[]|.[0]?|.value.createdAt"`
body_handle=$handle
body_pds=$pds
if [ "$old_handle" != "null" ];then
body_handle="$old_handle -> $handle"
fi
if [ "$old_pds" != "null" ];then
body_pds="$old_pds -> $pds"
fi
old_pds=`curl -sL https://plc.directory/$did/log|jq -r ".[0]|.services.atproto_pds.endpoint"|cut -d / -f 3-`
old_handle=`curl -sL https://plc.directory/$did/log|jq -r ".[0]|.alsoKnownAs.[0]"|cut -d / -f 3-`
if [ "$old_handle" != "null" ];then
body_handle="$old_handle -> $handle"
fi
if [ "$old_pds" != "null" ];then
body_pds="$old_pds -> $pds"
fi
if [ "$old_pds" = "$pds" ];then
body_pds=$pds
fi
if [ "$old_handle" = "$handle" ];then
body_handle=$handle
fi
echo pds : $body_pds
echo handle : $body_handle
echo did : $did
echo createdAt : $first_post

View File

@@ -0,0 +1,18 @@
name: Gitea Actions Demo
run-name: ${{ gitea.actor }} is testing out Gitea Actions 🚀
on: [push]
jobs:
Explore-Gitea-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
- run: echo "🔎 The name of your branch is ${{ gitea.ref }} and your repository is ${{ gitea.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ gitea.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ gitea.workspace }}
- run: echo "🍏 This job's status is ${{ gitea.status }}."

12
.gitignore vendored
View File

@@ -1,9 +1,19 @@
Cargo.lock Cargo.lock
target target
*.json #*.json
*.DS_Store *.DS_Store
**.DS_Store **.DS_Store
scpt/json/ scpt/json/
.config/ai/*.toml .config/ai/*.toml
.config/ai/*.json .config/ai/*.json
.config/ai/txt .config/ai/txt
.config/ai/png
.ssh/*.key
.ssh/*.pub
.ssh/*config
.env
pnpm-lock.yaml
**Cargo.lock
*/target/
*/**/*.rs.bk

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "bot_scpt"]
path = .config/ai/scpt
url = git@git.syui.ai:ai/bot_scpt.git

0
.ssh/keep Normal file
View File

View File

@@ -1,13 +1,22 @@
[package] [package]
name = "ai" name = "aibot"
version = "0.0.1" authors = ["syui"]
version = "0.1.0"
edition = "2021" edition = "2021"
description = "ai.bot - Bluesky AT Protocol Bot"
[[bin]]
name = "aibot"
path = "src/main.rs"
[[bin]]
name = "ai"
path = "src/alias.rs"
[dependencies] [dependencies]
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 = "*"
@@ -17,3 +26,7 @@ rustc-serialize = "*"
toml = "*" toml = "*"
iso8601-timestamp = "*" iso8601-timestamp = "*"
sysinfo = "*" sysinfo = "*"
[dev-dependencies]
mockito = "1.2"
tokio-test = "0.4"

View File

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

View File

@@ -17,6 +17,14 @@ command = "cargo"
args = ["test"] args = ["test"]
dependencies = ["clean"] dependencies = ["clean"]
[tasks.test-quick]
command = "cargo"
args = ["test"]
[tasks.test-verbose]
command = "cargo"
args = ["test", "--", "--nocapture"]
[tasks.my-flow] [tasks.my-flow]
dependencies = [ dependencies = [
"format", "format",

View File

@@ -3,14 +3,18 @@
<img src="./icon/avatar.png" width="100"> <img src="./icon/avatar.png" width="100">
- name : ai bot - name : ai bot
- base : [rust](https://www.rust-lang.org) - base : [ai os](https://git.syui.ai/ai/os)
- host : [yui.syui.ai](https://bsky.app/profile/yui.syui.ai), [ai.syu.is](https://web.syu.is/profile/ai.syu.is) - host : [yui.syui.ai](https://bsky.app/profile/yui.syui.ai), [ai.syu.is](https://web.syu.is/profile/ai.syu.is)
```sh ```sh
$ ai $ ai
``` ```
### logo ```sh
$ docker run -it syui/aios ai
```
### build
```sh ```sh
$ cargo build $ cargo build
@@ -57,17 +61,21 @@ $ ai n | jq .
$ ai bot $ ai bot
``` ```
|command|sub|type|body| |command|sub|type|link|auth|
|---|---|---|---| |---|---|---|---|---|
|@yui.syui.ai did||mention, reply| [plc.directory](https://plc.directory)/$did/log | |/did||mention, reply| [plc.directory](https://plc.directory)/$did/log |user|
|@yui.syui.ai card|r, s, b|mention, reply| https://card.syui.ai | |/card|r, s, b|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|@yui.syui.ai ten|start, d, p|mention, reply| https://card.syui.ai | |/ten|start, close, d, p|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|@yui.syui.ai fav|{cid}|mention, reply| https://card.syui.ai | |/fav|{cid}|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|@yui.syui.ai egg|{password}|mention, reply| https://card.syui.ai | |/egg|{password}|mention, reply| [card.syui.ai](https://card.syui.ai) |user|
|@yui.syui.ai 占い||mention, reply| https://yui.syui.ai | |/nyan|🍬|mention, reply| [yui.syui.ai](https://yui.syui.ai) |user|
|@yui.syui.ai nyan|🍰|mention, reply| https://yui.syui.ai | |/diffusers|{keyword}|mention, reply| [huggingface.co/diffusers](https://huggingface.co/docs/diffusers/index) |user|
|@yui.syui.ai diffusers|{keyword}|mention, reply| [diffusers](https://huggingface.co/docs/diffusers/index) | |/sh|{command}|mention, reply| [archlinux.org](https://wiki.archlinux.org/title/Systemd-nspawn) |admin|
|@yui.syui.ai sh(admin)|{command}|mention, reply| [archlinux](https://wiki.archlinux.org/title/Systemd-nspawn) | |/占い||mention, reply| [yui.syui.ai](https://yui.syui.ai) |user|
```sh
@yui.syui.ai /did
```
### test ### test
@@ -86,13 +94,51 @@ $ cargo make build
### docker ### docker
> .env
```sh ```sh
$ docker run -it syui/aios ai HANDLE=ai.syu.is
$ docker run -it -d syui/aios zsh -c "ai login <handle> -p <password> && ai bot" PASSWORD=xxx
HOST=syu.is
ADMIN=syui.syu.is
``` ```
```sh ```sh
$ cp -rf ~/.config/ai ./.config/ $ docker compose build
$ docker compose up -d
$ docker compose up
``` ```
## pds:card
- https://atproto.com/ja/guides/lexicon
- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.card/3lagpwihqxi2v
```sh
# oauth(button)
[yui]ai.syui.card.verify -> [user]ai.syui.card
[yui]
$ ./target/debug/ai card-verify -i 0 -p 0 -r 0 -h syui.ai -d did:plc:uqzpqmrjnptsxezjx4xuh2mn
{"uri":"at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q"}
[user]
$ ./target/debug/ai card -i 0 -p 0 -r 0 -v at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q
```
## pds:game
- https://atproto.com/ja/specs/record-key
- https://at.syu.is/at/did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.syui.game/self
```sh
# oauth(play)
[yui]ai.syui.game.user -> [user]ai.syui.game
[account]
# https://at.syu.is/at/did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui
## [rkey]
1. echo $handle|cut -d . -f 1
2. $handle
3. tid
```

10
at/feed-generator/env Normal file
View File

@@ -0,0 +1,10 @@
FEEDGEN_PORT=3000
FEEDGEN_LISTENHOST="0.0.0.0"
FEEDGEN_SQLITE_LOCATION="/data/db.sqlite"
FEEDGEN_SUBSCRIPTION_ENDPOINT="wss://bgs.syu.is"
FEEDGEN_PUBLISHER_DID="did:web:feed.syu.is"
FEEDGEN_HOSTNAME="feed.syu.is"
FEEDGEN_SUBSCRIPTION_RECONNECT_DELAY=3000
FEEDGEN_PUBLISHER_DID=did:plc:4hqjfn7m6n5hno3doamuhgef
FEEDGEN_SUBSCRIPTION_ENDPOINT="wss://bsky.network"

View File

@@ -0,0 +1,26 @@
# custom feed
- at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd
- [bsky.app](https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef/feed/cmd)
- [app.bsky.feed.getFeedSkeleton](https://feed.syu.is/xrpc/app.bsky.feed.getFeedSkeleton?feed=at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd)
```sh
did=did:plc:4hqjfn7m6n5hno3doamuhgef
col=app.bsky.feed.generator
cid=cmd
uri=at://$did/$col/$cid
echo $uri
```
## bsky-feed
```sh
$ git clone https://github.com/bluesky-social/feed-generator
```
```sh
docker compose build feed-generator
docker build -t publish_feed -f Dockerfile.feed .
docker run publish_feed
```

View File

@@ -0,0 +1,43 @@
import { InvalidRequestError } from '@atproto/xrpc-server'
import { QueryParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
import { AppContext } from '../config'
// max 15 chars
export const shortname = 'cmd'
export const handler = async (ctx: AppContext, params: QueryParams) => {
let builder = ctx.db
.selectFrom('post')
.selectAll()
.orderBy('indexedAt', 'desc')
.orderBy('cid', 'desc')
.limit(params.limit)
if (params.cursor) {
const [indexedAt, cid] = params.cursor.split('::')
if (!indexedAt || !cid) {
throw new InvalidRequestError('malformed cursor')
}
const timeStr = new Date(parseInt(indexedAt, 10)).toISOString()
builder = builder
.where('post.indexedAt', '<', timeStr)
.orWhere((qb) => qb.where('post.indexedAt', '=', timeStr))
.where('post.cid', '<', cid)
}
const res = await builder.execute()
const feed = res.map((row) => ({
post: row.uri,
}))
let cursor: string | undefined
const last = res.at(-1)
if (last) {
cursor = `${new Date(last.indexedAt).getTime()}::${last.cid}`
}
return {
cursor,
feed,
}
}

View File

@@ -0,0 +1,14 @@
import { AppContext } from '../config'
import {
QueryParams,
OutputSchema as AlgoOutput,
} from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
import * as cmd from './cmd'
type AlgoHandler = (ctx: AppContext, params: QueryParams) => Promise<AlgoOutput>
const algos: Record<string, AlgoHandler> = {
[cmd.shortname]: cmd.handler,
}
export default algos

View File

@@ -0,0 +1,50 @@
import {
OutputSchema as RepoEvent,
isCommit,
} from './lexicon/types/com/atproto/sync/subscribeRepos'
import { FirehoseSubscriptionBase, getOpsByType } from './util/subscription'
export class FirehoseSubscription extends FirehoseSubscriptionBase {
async handleEvent(evt: RepoEvent) {
if (!isCommit(evt)) return
const ops = await getOpsByType(evt)
// This logs the text of every post off the firehose.
// Just for fun :)
// Delete before actually using
for (const post of ops.posts.creates) {
console.log(post.record.text)
}
const postsToDelete = ops.posts.deletes.map((del) => del.uri)
const postsToCreate = ops.posts.creates
.filter((create) => {
return create.record.text.match('^/[a-z]') || create.record.text.match('^@ai ') || create.record.text.match('/ai ');
//return create.record.text.toLowerCase().includes('alf')
})
.map((create) => {
// map alf-related posts to a db row
return {
uri: create.uri,
cid: create.cid,
replyParent: create.record?.reply?.parent.uri ?? null,
replyRoot: create.record?.reply?.root.uri ?? null,
indexedAt: new Date().toISOString(),
}
})
if (postsToDelete.length > 0) {
await this.db
.deleteFrom('post')
.where('uri', 'in', postsToDelete)
.execute()
}
if (postsToCreate.length > 0) {
await this.db
.insertInto('post')
.values(postsToCreate)
.onConflict((oc) => oc.doNothing())
.execute()
}
}
}

View File

@@ -1,7 +1,11 @@
services: services:
aios: aios:
image: syui/aios #image: syui/aios
#command: ai bot -a syui.syu.is
build:
context: .
restart: always restart: always
env_file:
- .env
volumes: volumes:
- ./.config:/root/.config - ./.config:/root/.config
command: zsh -c "ai bot"

117
docs/README.md Normal file
View File

@@ -0,0 +1,117 @@
# ai.bot ドキュメント
ai.botプロジェクトの包括的なドキュメント集です。
## ドキュメント一覧
### 開発者向け
1. **[開発ガイド](./development-guide.md)**
- プロジェクト概要とアーキテクチャ
- 開発環境のセットアップ
- 開発ワークフローとベストプラクティス
- 新機能追加の手順
2. **[HTTPクライアントAPI](./http-client-api.md)**
- HttpClientモジュールの完全なAPIリファレンス
- 使用例とサンプルコード
- エラーハンドリングのベストプラクティス
### 保守・運用向け
3. **[移行ガイド](./migration-guide.md)**
- パッケージ名・CLI名の変更詳細
- 段階的移行手順
- 後方互換性の説明
- トラブルシューティング
4. **[リファクタリングサマリー](./refactoring-summary.md)**
- HTTPクライアント共通化の詳細
- エラーハンドリング改善の記録
- 対象ファイル一覧とBefore/After
## クイックスタート
### 1. 基本セットアップ
```bash
# 依存関係インストール
cargo install cargo-make
# プロジェクトビルド
cargo build
# テスト実行
cargo test
```
### 2. CLI使用方法
```bash
# 新しいコマンド(推奨)
./target/debug/aibot --help
# 旧コマンド(互換性)
./target/debug/ai --help
```
### 3. 設定ディレクトリ
- **新**: `~/.config/syui/ai/bot/`
- **旧**: `~/.config/ai/` (自動移行対応)
## 主要な変更履歴
### 2025年6月6日 - 大規模リファクタリング
#### HTTPクライアント共通化
- 24個のファイルをHttpClientモジュールで統合
- コード重複を大幅削減
- エラーハンドリングを改善(`.unwrap()``match`
#### 命名規則統一
- パッケージ名: `ai``aibot`
- CLI名: `ai``aibot``ai`は互換性維持)
- 設定ディレクトリ: `~/.config/ai/``~/.config/syui/ai/bot/`
#### テストインフラ構築
- ユニットテストの追加
- cargo-makeによるタスク管理
- CI/CD対応の準備
## アーキテクチャ概要
```
ai.bot/
├── src/
│ ├── main.rs # メインCLI (aibot)
│ ├── alias.rs # 互換性CLI (ai)
│ ├── http_client.rs # 統合HTTPクライアント
│ ├── data.rs # 設定・データ管理
│ ├── game/ # ゲーム機能
│ └── tests/ # テストスイート
├── docs/ # ドキュメント
├── scripts/ # セットアップスクリプト
└── ~/.config/syui/ai/bot/ # 設定ディレクトリ
├── scpt/ # コマンドスクリプト
└── txt/ # ログファイル
```
## サポート・問い合わせ
### 開発関連
- 新機能追加: [開発ガイド](./development-guide.md)を参照
- API使用方法: [HTTPクライアントAPI](./http-client-api.md)を参照
### 移行・運用関連
- 移行作業: [移行ガイド](./migration-guide.md)を参照
- トラブル: 各ドキュメントのトラブルシューティング章を参照
### 履歴・詳細
- 実装詳細: [リファクタリングサマリー](./refactoring-summary.md)を参照
## 貢献ガイドライン
1. **コードスタイル**: `cargo fmt`でフォーマット必須
2. **テスト**: 新機能には対応するテストを追加
3. **エラーハンドリング**: `.unwrap()`の使用禁止
4. **ドキュメント**: 重要な変更は対応ドキュメントも更新
詳細は[開発ガイド](./development-guide.md)の「コントリビューション」章を参照してください。

38
docs/atproto.md Normal file
View File

@@ -0,0 +1,38 @@
## curl
no-authorization
https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo
```sh
handle=yui.syui.ai
host=bsky.social
api=$host/xrpc
plc=plc.directory
url=$api/com.atproto.repo.describeRepo
curl -sL ${host}/xrpc/_health
d=`curl -sL "${url}?repo=$handle"`
echo $d
did=`echo $d|jq -r .did`
echo $did
collection=app.bsky.feed.post
url=$api/com.atproto.repo.listRecords
timed=`curl -sL "${url}?repo=$handle&collection=$collection&reverse=true&limit=1"|jq -r ".[]|.[0]?|.value.createdAt"`
cid=`curl -sL "${url}?repo=$handle&collection=$collection&reverse=true&limit=1"|jq -r ".[]|.[0]?|.cid"`
uri=`curl -sL "${url}?repo=$handle&collection=$collection&reverse=true&limit=1"|jq -r ".[]|.[0]?|.uri"`
rkey=`echo $uri|cut -d / -f 5`
url=$api/com.atproto.repo.getRecord
curl -sL "$url?repo=$did&collection=$collection&rkey=$rkey"|jq .
uri=at://did:plc:vjug55kidv6sye7ykr5faxxn/app.bsky.feed.post/3jzn6g7ixgq2y
cid=bafyreiey2tt4dhvuvr7tofatdverqrxmscnnus2uyfcmkacn2fov3vb4wa
did=did:plc:vjug55kidv6sye7ykr5faxxn
rkey=3jzn6g7ixgq2y
url=$api/com.atproto.repo.getRecord
curl -sL "$url?repo=$did&collection=$collection&rkey=$rkey&cid="|jq .
```

332
docs/development-guide.md Normal file
View File

@@ -0,0 +1,332 @@
# ai.bot 開発ガイド
## プロジェクト概要
ai.botは、Rust製のBlueskyAT Protocolボットです。メンション応答、コマンド実行、OpenAI統合などの機能を提供します。
**重要**: 2025年6月6日より、パッケージ名とCLI名が統一されました
- **新CLI名**: `aibot` (推奨)
- **旧CLI名**: `ai` (互換性維持)
- **設定ディレクトリ**: `~/.config/syui/ai/bot/` (旧: `~/.config/ai/`)
詳細は[移行ガイド](./migration-guide.md)を参照してください。
## アーキテクチャ
### 主要コンポーネント
1. **HTTPクライアント** (`src/http_client.rs`)
- AT Protocol API呼び出しの統一インターフェース
- 認証処理の自動化
- エラーハンドリングの標準化
2. **コマンドシステム** (`src/main.rs`)
- Seahorseを使用したCLIインターフェース
- 各機能への振り分け
3. **AT Protocolモジュール**
- 投稿、フォロー、いいね等の基本機能
- 認証・セッション管理
- フィード・通知処理
4. **ゲームシステム** (`src/game/`)
- カードゲーム機能
- ユーザー管理
- ゲームデータ処理
5. **外部連携**
- OpenAI API統合 (`src/openai.rs`)
- 画像処理機能
## 開発環境セットアップ
### 必要なツール
```bash
# Rust最新安定版
rustup update stable
# Cargo makeタスクランナー
cargo install cargo-make
# 開発用依存関係は自動インストール
```
**注意**: 初回は`cargo install cargo-make`でcargo-makeのインストールが必要です。
### 設定ファイル
```
~/.config/syui/ai/bot/config.toml # 基本設定
~/.config/syui/ai/bot/refresh # リフレッシュトークン
~/.config/syui/ai/bot/access # アクセストークン
```
**注意**: 旧設定ディレクトリ(`~/.config/ai/`)も自動的に参照・移行されます。
## 開発ワークフロー
### 1. コード変更
```bash
# フォーマット
cargo fmt
# コンパイル確認
cargo check
# テスト実行
cargo test
```
### 2. 統合ワークフロー
```bash
# 全体フロー(フォーマット→ビルド→テスト)
cargo make my-flow
```
### 3. 個別テスト
```bash
cargo make test-quick # 素早いテスト
cargo make test-verbose # 詳細出力テスト
```
## API呼び出しパターン
### HttpClientの使用方法
#### 基本的なGETリクエスト
```rust
use crate::http_client::HttpClient;
pub async fn get_user_profile(handle: String) -> String {
let client = HttpClient::new();
let url = format!("https://bsky.social/xrpc/app.bsky.actor.getProfile?actor={}", handle);
match client.get_with_auth(&url).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error getting profile: {}", e);
"err".to_string()
}
}
}
```
#### JSONを送信するPOSTリクエスト
```rust
use crate::http_client::HttpClient;
use serde_json::json;
pub async fn create_post(text: String) -> String {
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.repo.createRecord";
let payload = json!({
"repo": "user.handle",
"collection": "app.bsky.feed.post",
"record": {
"text": text,
"createdAt": chrono::Utc::now().to_rfc3339()
}
});
match client.post_json_with_auth(&url, &payload).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error creating post: {}", e);
"err".to_string()
}
}
}
```
## テストの書き方
### ユニットテスト
```rust
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_http_client_creation() {
let client = HttpClient::new();
// テストロジック
}
#[test]
fn test_data_parsing() {
// 同期テスト
}
}
```
### 統合テスト
```rust
#[tokio::test]
#[ignore] // 通常は無視、環境変数設定時のみ実行
async fn test_real_api() {
if std::env::var("RUN_INTEGRATION_TESTS").is_ok() {
// 実際のAPI呼び出しテスト
}
}
```
## エラーハンドリングガイドライン
### 推奨パターン
```rust
// Good: 適切なエラーハンドリング
match api_call().await {
Ok(response) => response,
Err(e) => {
eprintln!("Error in operation: {}", e);
"err".to_string()
}
}
// Bad: unwrapの使用
api_call().await.unwrap()
```
### エラーレスポンス
- API呼び出し失敗時は`"err"`文字列を返す
- ログにエラー詳細を出力
- 上位レイヤーでエラー処理を継続
## 新機能追加の手順
### 1. AT Protocol関連機能
```bash
# 1. モジュールファイルを作成
touch src/new_feature.rs
# 2. main.rsに追加
# pub mod new_feature;
# 3. HttpClientを使用して実装
# 4. テストを追加
# 5. main.rsのコマンドに追加
```
### 2. 基本的なテンプレート
```rust
use crate::http_client::HttpClient;
use crate::{data_toml, url};
use serde_json::json;
pub async fn new_feature_request(param: String) -> String {
let client = HttpClient::new();
let endpoint_url = url(&"endpoint_name");
let payload = json!({
"param": param
});
match client.post_json_with_auth(&endpoint_url, &payload).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error in new_feature: {}", e);
"err".to_string()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_new_feature() {
// テスト実装
}
}
```
## デバッグ方法
### 1. ログ出力
```rust
eprintln!("Debug: {}", variable);
println!("Info: {}", message);
```
### 2. テスト実行
```bash
# 特定のテストのみ
cargo test test_name
# 詳細出力
cargo test -- --nocapture
# 特定モジュール
cargo test module_name
```
### 3. HTTPリクエストのデバッグ
HttpClientモジュール内でリクエスト/レスポンスをログ出力することで、API呼び出しの詳細を確認できます。
## パフォーマンス考慮事項
### HttpClientの再利用
```rust
// Good: 一度作成して再利用
let client = HttpClient::new();
for item in items {
client.get_with_auth(&url).await;
}
// Bad: 毎回新規作成
for item in items {
let client = HttpClient::new();
client.get_with_auth(&url).await;
}
```
### 非同期処理
- 可能な限り並列処理を活用
- await呼び出しを最小限に
- tokio::joinやfutures::join_allの活用
## トラブルシューティング
### よくある問題
1. **認証エラー**
- トークンの有効期限切れ
- 設定ファイルの不備
2. **コンパイルエラー**
- 型不整合(特にライフタイム)
- 未使用のimport
3. **実行時エラー**
- ネットワーク接続問題
- API仕様変更
### 解決方法
```bash
# 設定確認(新ディレクトリ)
ls -la ~/.config/syui/ai/bot/
# 旧ディレクトリも確認
ls -la ~/.config/ai/
# トークンリフレッシュ
./target/debug/aibot refresh
# または互換性コマンド
./target/debug/ai refresh
# 詳細ログ
RUST_LOG=debug ./target/debug/aibot [command]
```
## コントリビューション
1. コードフォーマット必須: `cargo fmt`
2. テスト追加必須: 新機能には対応テスト
3. エラーハンドリング必須: `.unwrap()`の使用禁止
4. ドキュメント更新: 重要な変更は本ドキュメントも更新
## 関連ドキュメント
- [リファクタリングサマリー](./refactoring-summary.md)
- [AT Protocol仕様](https://atproto.com/)
- [Rust公式ドキュメント](https://doc.rust-lang.org/)

334
docs/http-client-api.md Normal file
View File

@@ -0,0 +1,334 @@
# HttpClient API リファレンス
## 概要
`HttpClient`は、AT Protocol APIへの統一されたHTTPクライアントインターフェースです。認証、エラーハンドリング、リクエスト管理を自動化します。
## 基本的な使用方法
```rust
use crate::http_client::HttpClient;
let client = HttpClient::new();
// または
let client = HttpClient::default();
```
## APIメソッド
### 認証付きリクエスト
#### get_with_auth
AT Protocolの認証が必要なGETリクエストを実行します。
```rust
pub async fn get_with_auth(&self, url: &str) -> Result<String, Error>
```
**パラメータ:**
- `url`: リクエスト先のURL
**戻り値:**
- `Ok(String)`: レスポンスボディ(文字列)
- `Err(Error)`: リクエストエラー
**使用例:**
```rust
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/app.bsky.actor.getProfile?actor=user.bsky.social";
match client.get_with_auth(&url).await {
Ok(response) => println!("Response: {}", response),
Err(e) => eprintln!("Error: {}", e),
}
```
#### post_json_with_auth
AT Protocolの認証が必要なPOSTリクエストJSONを実行します。
```rust
pub async fn post_json_with_auth<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error>
```
**パラメータ:**
- `url`: リクエスト先のURL
- `json`: シリアライズ可能なJSONデータ
**戻り値:**
- `Ok(String)`: レスポンスボディ(文字列)
- `Err(Error)`: リクエストエラー
**使用例:**
```rust
use serde_json::json;
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.repo.createRecord";
let payload = json!({
"repo": "user.bsky.social",
"collection": "app.bsky.feed.post",
"record": {
"text": "Hello, World!",
"createdAt": "2025-01-01T00:00:00Z"
}
});
match client.post_json_with_auth(&url, &payload).await {
Ok(response) => println!("Post created: {}", response),
Err(e) => eprintln!("Error: {}", e),
}
```
#### delete_with_auth
AT Protocolの認証が必要なDELETEリクエストを実行します。
```rust
pub async fn delete_with_auth(&self, url: &str) -> Result<String, Error>
```
**パラメータ:**
- `url`: リクエスト先のURL
**戻り値:**
- `Ok(String)`: レスポンスボディ(文字列)
- `Err(Error)`: リクエストエラー
**使用例:**
```rust
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.repo.deleteRecord";
match client.delete_with_auth(&url).await {
Ok(response) => println!("Deleted: {}", response),
Err(e) => eprintln!("Error: {}", e),
}
```
### 認証なしリクエスト
#### get
認証なしのGETリクエストを実行します。
```rust
pub async fn get(&self, url: &str) -> Result<String, Error>
```
**使用例:**
```rust
let client = HttpClient::new();
let url = "https://public-api.example.com/data";
match client.get(&url).await {
Ok(response) => println!("Response: {}", response),
Err(e) => eprintln!("Error: {}", e),
}
```
#### post_json
認証なしのPOSTリクエストJSONを実行します。ログイン処理などで使用。
```rust
pub async fn post_json<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error>
```
**使用例:**
```rust
use serde_json::json;
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.session.create";
let credentials = json!({
"identifier": "user.bsky.social",
"password": "password"
});
match client.post_json(&url, &credentials).await {
Ok(response) => println!("Login successful: {}", response),
Err(e) => eprintln!("Login failed: {}", e),
}
```
### カスタムヘッダー付きリクエスト
#### post_with_headers
カスタムヘッダーを指定したPOSTリクエストを実行します。
```rust
pub async fn post_with_headers<T: Serialize>(
&self,
url: &str,
json: &T,
headers: Vec<(&str, &str)>
) -> Result<String, Error>
```
**パラメータ:**
- `url`: リクエスト先のURL
- `json`: シリアライズ可能なJSONデータ
- `headers`: ヘッダーのキーと値のペアのベクター
**使用例:**
```rust
use serde_json::json;
let client = HttpClient::new();
let url = "https://bsky.social/xrpc/com.atproto.session.refresh";
let refresh_token = "refresh_token_value";
let auth_header = format!("Bearer {}", refresh_token);
let headers = vec![("Authorization", auth_header.as_str())];
let empty_json = json!({});
match client.post_with_headers(&url, &empty_json, headers).await {
Ok(response) => println!("Token refreshed: {}", response),
Err(e) => eprintln!("Refresh failed: {}", e),
}
```
## 内部実装詳細
### 認証処理
認証付きメソッドは自動的に以下を実行します:
1. `data_refresh(&"access")`でアクセストークンを取得
2. `Authorization: Bearer {token}`ヘッダーを自動追加
3. リクエストを実行
### エラーハンドリング
- ネットワークエラー
- HTTPステータスエラー
- JSON解析エラー
- タイムアウトエラー
すべて`reqwest::Error`として統一されて返されます。
## 使用上の注意
### 1. トークン管理
- アクセストークンは自動的に取得されます
- トークンの有効期限切れは呼び出し元で処理する必要があります
- リフレッシュトークンは`post_with_headers`で手動設定
### 2. エラー処理パターン
```rust
// 推奨パターン
match client.get_with_auth(&url).await {
Ok(response) => {
// 成功処理
response
},
Err(e) => {
eprintln!("API call failed: {}", e);
"err".to_string() // 既存コードとの互換性
}
}
```
### 3. パフォーマンス
- `HttpClient`の作成は軽量な操作です
- 内部でrequwestクライアントを再利用しています
- 複数のリクエストで同じインスタンスを使い回すことも可能
### 4. デバッグ
リクエスト/レスポンスの詳細をログ出力したい場合:
```rust
// HttpClientモジュール内で適宜printlnを追加
println!("Request URL: {}", url);
println!("Response: {}", response);
```
## 実装例新しいAPI呼び出しモジュール
```rust
use crate::http_client::HttpClient;
use crate::{data_toml, url};
use serde_json::json;
use iso8601_timestamp::Timestamp;
pub async fn create_custom_record(data: String) -> String {
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let timestamp = Timestamp::now_utc().to_string();
let payload = json!({
"repo": handle,
"did": did,
"collection": "app.bsky.custom.record",
"record": {
"data": data,
"createdAt": timestamp
}
});
let client = HttpClient::new();
match client.post_json_with_auth(&url, &payload).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error creating custom record: {}", e);
"err".to_string()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_create_custom_record() {
let result = create_custom_record("test data".to_string()).await;
assert_ne!(result, "err");
}
}
```
## 移行ガイド
### 既存コードからの移行
#### Before (旧実装)
```rust
extern crate reqwest;
use crate::data_refresh;
pub async fn old_request() -> String {
let token = data_refresh(&"access");
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&data)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
res
}
```
#### After (新実装)
```rust
use crate::http_client::HttpClient;
pub async fn new_request() -> String {
let client = HttpClient::new();
match client.post_json_with_auth(&url, &data).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error in request: {}", e);
"err".to_string()
}
}
}
```
### チェックリスト
- [ ] `extern crate reqwest;`を削除
- [ ] `use crate::http_client::HttpClient;`を追加
- [ ] `data_refresh(&"access")`の手動呼び出しを削除
- [ ] `reqwest::Client::new()``HttpClient::new()`に置換
- [ ] `.unwrap()`を適切な`match`文に置換
- [ ] エラーメッセージの追加

271
docs/migration-guide.md Normal file
View File

@@ -0,0 +1,271 @@
# ai.bot 段階的移行ガイド
## 概要
ai.botプロジェクトの命名規則とディレクトリ構造を統一するための段階的移行作業の記録です。
## 移行内容
### 1. パッケージ・CLI名の変更
| 項目 | 変更前 | 変更後 |
|------|--------|--------|
| パッケージ名 | `ai` | `aibot` |
| メインCLI | `ai` | `aibot` |
| 互換性CLI | - | `ai` (aibotへのエイリアス) |
### 2. ディレクトリ構造の変更
| 用途 | 変更前 | 変更後 |
|------|--------|--------|
| 設定ディレクトリ | `~/.config/ai/` | `~/.config/syui/ai/bot/` |
| ログディレクトリ | `~/.config/ai/txt/` | `~/.config/syui/ai/bot/txt/` |
| スクリプトディレクトリ | `~/.config/ai/scpt/` | `~/.config/syui/ai/bot/scpt/` |
## 実装した機能
### 1. デュアルバイナリシステム
#### Cargo.toml設定
```toml
[package]
name = "aibot"
description = "ai.bot - Bluesky AT Protocol Bot"
[[bin]]
name = "aibot"
path = "src/main.rs"
[[bin]]
name = "ai"
path = "src/alias.rs"
```
#### エイリアスバイナリ (src/alias.rs)
- `ai`コマンドが`aibot`を自動的に呼び出し
- 同一ディレクトリ内のaibotバイナリを優先検索
- PATH内のaibotもフォールバック対応
### 2. 設定ディレクトリの自動移行
#### data_file関数の改良
```rust
pub fn data_file(s: &str) -> String {
// 新しい設定ディレクトリ(優先)
let new_config_dir = "/.config/syui/ai/bot/";
// 旧設定ディレクトリ(互換性のため)
let old_config_dir = "/.config/ai/";
// 自動移行ロジック
// 1. 新しいパスにファイルが存在 → 新しいパスを使用
// 2. 旧パスのみに存在 → 新しいパスにコピーして使用
// 3. どちらにも存在しない → 新しいパスを使用
}
```
#### log_file関数も同様の移行対応
### 3. 移行セットアップスクリプト
#### scripts/setup-migration.sh
```bash
#!/bin/bash
# 新しい設定ディレクトリの作成
mkdir -p ~/.config/syui/ai/bot/
# スクリプトディレクトリのコピー
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
# エイリアス設定の提案
echo "alias ai='aibot'"
```
## 後方互換性
### 1. 設定ファイル
- **自動移行**: 旧パスから新パスへ自動コピー
- **継続読み込み**: 移行後も旧パスは参照可能
- **透過的**: ユーザーは変更を意識する必要なし
### 2. コマンドライン
- **aiコマンド**: 既存スクリプトで引き続き使用可能
- **aibotコマンド**: 新しい正式名称
- **完全互換**: 引数、オプション、出力すべて同一
### 3. スクリプト
- **shellscript**: `ai`コマンドをそのまま使用可能
- **エイリアス推奨**: `alias ai='aibot'`で統一
## 移行手順
### 1. 自動移行(推奨)
```bash
# プロジェクトをビルド
cargo build
# 移行スクリプトを実行
./scripts/setup-migration.sh
# 新しいバイナリを使用
./target/debug/aibot --help
./target/debug/ai --help # 互換性確認
```
### 2. 手動移行
```bash
# 1. 新しい設定ディレクトリ作成
mkdir -p ~/.config/syui/ai/bot/
# 2. スクリプトディレクトリのコピー
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
# 3. バイナリのインストール
cargo install --path .
# 4. エイリアス設定(オプション)
echo "alias ai='aibot'" >> ~/.zshrc
```
### 3. 段階的移行(企業環境等)
```bash
# Phase 1: 新しいバイナリの導入
cargo install --path . --bin aibot
# Phase 2: エイリアス設定
echo "alias ai='aibot'" >> ~/.profile
# Phase 3: スクリプトの段階的更新
# (既存スクリプトは変更不要)
# Phase 4: 旧設定の完全移行(任意のタイミング)
rm -rf ~/.config/ai/ # 十分な検証後
```
## 影響範囲
### 1. 変更が必要な箇所
-**なし** (完全後方互換)
### 2. 変更が推奨される箇所
- 📝 shellrcでのエイリアス設定
- 📝 ドキュメント内のコマンド例
- 📝 CI/CDスクリプト新しい名前使用
### 3. 変更が不要な箇所
- ✅ 既存のshellscript
- ✅ 設定ファイル
- ✅ ログファイル
- ✅ git submodule
## トラブルシューティング
### 1. 設定ファイルが見つからない
```bash
# 現在の設定ディレクトリを確認
ls -la ~/.config/syui/ai/bot/
ls -la ~/.config/ai/ # 旧ディレクトリ
# 手動でコピー
cp ~/.config/ai/token.json ~/.config/syui/ai/bot/
```
### 2. aiコマンドが動作しない
```bash
# aibotバイナリの存在確認
which aibot
./target/debug/aibot --help
# エイリアスの設定
alias ai='aibot'
# または
export PATH="$(pwd)/target/debug:$PATH"
```
### 3. スクリプトディレクトリが見つからない
```bash
# 手動でコピー
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
# gitサブモジュールの場合
cd ~/.config/syui/ai/bot/scpt
git remote -v # リモートURLを確認
```
## 検証方法
### 1. 基本動作確認
```bash
# 新しいコマンド
aibot --help
aibot post "Test from aibot"
# 互換性確認
ai --help
ai post "Test from ai alias"
# 設定ディレクトリ確認
ls -la ~/.config/syui/ai/bot/
```
### 2. 自動移行確認
```bash
# 旧設定で動作確認
touch ~/.config/ai/test_file
aibot some_command # 新ディレクトリにコピーされることを確認
ls -la ~/.config/syui/ai/bot/test_file
```
### 3. スクリプト互換性確認
```bash
# 既存スクリプトの動作確認
cd ~/.config/syui/ai/bot/scpt
./ai.zsh # aiコマンドが正常動作することを確認
```
## 今後の予定
### Phase 1 (完了)
- ✅ パッケージ名の変更
- ✅ デュアルバイナリシステム
- ✅ 設定ディレクトリの自動移行
- ✅ 後方互換性の確保
### Phase 2 (将来)
- 📅 ドキュメントの更新
- 📅 CI/CDの新コマンド対応
- 📅 パフォーマンス最適化
### Phase 3 (任意)
- 📅 旧設定ディレクトリの削除
- 📅 エイリアスバイナリの削除
- 📅 完全な新体系への移行
## 関連ファイル
- `Cargo.toml` - パッケージ設定
- `src/alias.rs` - エイリアスバイナリ
- `src/data.rs` - 設定ディレクトリ管理
- `scripts/setup-migration.sh` - 移行スクリプト
- `docs/migration-guide.md` - 本ドキュメント
## 注意事項
1. **gitサブモジュール**: scptディレクトリがgitサブモジュールの場合、リモートURLの確認が必要
2. **権限**: 設定ディレクトリの権限は適切に設定すること
3. **バックアップ**: 重要な設定は移行前にバックアップを推奨
4. **テスト**: 本番環境での使用前に十分なテストを実施
## 参考情報
- [開発ガイド](./development-guide.md)
- [リファクタリングサマリー](./refactoring-summary.md)
- [HTTPクライアントAPI](./http-client-api.md)

208
docs/refactoring-summary.md Normal file
View File

@@ -0,0 +1,208 @@
# ai.bot リファクタリング作業サマリー
## 概要
2025年6月6日に実施されたai.botプロジェクトのリファクタリング作業の完全な記録です。
## 実施した作業
### 1. HTTPクライアントの共通化
- **作成したファイル**: `src/http_client.rs`
- **対象**: 24個のファイルを統合
- **削減**: 重複するHTTPリクエストコードを一箇所に集約
#### HttpClientモジュールの機能
```rust
// 認証付きリクエスト
client.get_with_auth(&url).await
client.post_json_with_auth(&url, &json_data).await
client.delete_with_auth(&url).await
// 認証なしリクエスト
client.get(&url).await
client.post_json(&url, &json_data).await
// カスタムヘッダー付きリクエスト
client.post_with_headers(&url, &json_data, headers).await
```
### 2. リファクタリング対象ファイル一覧
#### コアAPIファイル (8個)
- `src/post.rs` - 投稿作成
- `src/like.rs` - いいね機能
- `src/repost.rs` - リポスト機能
- `src/follow.rs` - フォロー/アンフォロー
- `src/reply.rs` - 返信機能
- `src/profile.rs` - プロフィール取得
- `src/delete_record.rs` - レコード削除
- `src/describe.rs` - ユーザー説明取得
#### 認証・セッション管理 (3個)
- `src/session.rs` - セッション管理
- `src/refresh.rs` - トークンリフレッシュ
- `src/token.rs` - ログイン認証
#### 通知・メンション (3個)
- `src/mention.rs` - メンション投稿
- `src/notify.rs` - 通知取得
- `src/notify_read.rs` - 通知既読
#### フィード・タイムライン (4個)
- `src/feed_get.rs` - フィード取得
- `src/timeline_author.rs` - 作者タイムライン
- `src/followers.rs` - フォロワー取得
- `src/follows.rs` - フォロー取得
#### 画像関連 (3個)
- `src/img.rs` - 画像投稿
- `src/img_reply.rs` - 画像付き返信
- `src/img_upload.rs` - 画像アップロード
#### リンク・リッチテキスト (3個)
- `src/post_link.rs` - リンク付き投稿
- `src/reply_link.rs` - リンク付き返信
- `src/reply_og.rs` - OGデータ付き返信
#### ゲームモジュール (5個)
- `src/game/post_card.rs` - ゲームカード投稿
- `src/game/post_card_verify.rs` - カード検証
- `src/game/post_game.rs` - ゲームデータ投稿
- `src/game/post_game_login.rs` - ゲームログイン
- `src/game/post_game_user.rs` - ゲームユーザーデータ
### 3. 除外したファイル
- `src/openai.rs` - OpenAI API用異なる認証方式のため
- `src/feed_watch.rs` - reqwest使用していないため
### 4. 共通の変更パターン
#### Before (変更前)
```rust
extern crate reqwest;
use crate::data_refresh;
pub async fn some_request() -> String {
let token = data_refresh(&"access");
let client = reqwest::Client::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
res
}
```
#### After (変更後)
```rust
use crate::http_client::HttpClient;
pub async fn some_request() -> String {
let client = HttpClient::new();
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => {
eprintln!("Error: {}", e);
"err".to_string()
}
}
}
```
### 5. テストインフラの追加
#### 作成したテストファイル
- `src/tests/mod.rs` - テストモジュール宣言
- `src/tests/http_client_tests.rs` - HttpClientのテスト
#### テストコマンド
```bash
# 基本テスト
cargo test
# Makefileを使用したテスト
cargo make test # cleanしてからテスト
cargo make test-quick # 素早いテスト
cargo make test-verbose # 詳細出力
```
#### 追加した依存関係 (Cargo.toml)
```toml
[dev-dependencies]
mockito = "1.2"
tokio-test = "0.4"
```
## 改善された点
### コード品質
- **コード重複の削除**: 同じHTTPリクエストパターンの重複を排除
- **エラーハンドリング**: `.unwrap()`を適切な`match`文に置換
- **保守性**: HTTP関連のロジックが一箇所に集約
### 開発効率
- **変更容易性**: HTTPクライアントの変更が1ファイルで完結
- **テスト可能性**: ユニットテストの追加でバグ検出が容易
- **デバッグ性**: エラーメッセージの改善
### 安定性
- **パニック回避**: `.unwrap()`によるパニックを防止
- **エラー処理**: 適切なエラーレスポンスの返却
## 次のステップ(推奨)
### 1. bot.rsのリファクタリング (高優先度)
- 500行以上の巨大な関数を分割
- コマンド処理の構造化
- 深いif-else文の改善
### 2. 設定管理の統一 (中優先度)
- 複数の設定構造体の統合
- 設定ファイルパスの一元管理
### 3. main.rsの整理 (中優先度)
- コマンド定義の外部ファイル化
- モジュール構造の改善
## ファイル構造
```
src/
├── http_client.rs # 新規作成共通HTTPクライアント
├── tests/ # 新規作成:テストディレクトリ
│ ├── mod.rs
│ └── http_client_tests.rs
├── main.rs # 更新http_clientモジュール追加
├── Cargo.toml # 更新:テスト依存関係追加
├── Makefile.toml # 更新:テストコマンド追加
└── [24個のリファクタリング済みファイル]
```
## 注意事項
1. **OpenAI API**: `src/openai.rs`は意図的に除外(異なる認証方式)
2. **後方互換性**: 既存のAPI呼び出しは同じ形式を維持
3. **エラー処理**: "err"文字列を返すパターンは既存仕様に合わせて維持
## 検証方法
```bash
# コンパイル確認
cargo check
# テスト実行
cargo test
# フォーマット確認
cargo fmt --check
# 全体ビルド
cargo build
```
この作業により、ai.botプロジェクトのHTTP通信部分が大幅に改善され、今後の開発・保守が容易になりました。

View File

@@ -1 +1,230 @@
## ## test-notify
```sh
./target/debug/ai n|jq -r ".notifications|.[].cid" >> ~/.config/ai/txt/notify_cid*
./target/debug/ai bot
```
## docker
```sh
$ docker run -it syui/aios ai
$ docker run -it -d syui/aios zsh -c "ai login <handle> -p <password> && ai bot"
```
```sh
$ cp -rf ~/.config/ai ./.config/
$ docker compose up
```
## cron
```sh
$ sudo pacman -S fcron
$ fcrontab -e
* * * * * $HOME/bot/test/ai.zsh c
```
## ssh
```sh
$ ssh-keygen -f /.ssh/diffusers.key -t ed25519
```
```sh
FROM syui/aios
ADD .ssh /root/.ssh
```
```sh
Host diffusers
HostName localhost
User root
IdentityFile ~/.ssh/diffusers.key
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
```
```sh
services:
aios:
#image: syui/aios
build:
context: .
restart: always
volumes:
- ./.config:/root/.config
command: ai bot -a syui.syu.is
```
## openapi
```sh
# https://github.com/rdmurphy/atproto-openapi-types
$ curl -sLO https://raw.githubusercontent.com/rdmurphy/atproto-openapi-types/main/spec/api.json
```
## plc
```sh
# 何度か実行するとplcをlatestまでexportされる
$ .config/ai/scpt/test/pds.zsh e
```
## cmt
blogなどにblueskyアカウントのpostを表示します。
以下でbotがblogのコメントシステムを開きます。
```sh
@yui.syui.ai /comment https://syui.ai/blog/post/2024/04/25/bluesky/
```
開いたbotのpostに返信することで、特定のblog path上でpostを表示します。
<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kqxbtmwlje2h" data-bluesky-cid="bafyreiasxp5g3nkkd6g7lxh55qaxcc7ylefaljmbcp627nu2geks62c57m"><p lang="">please reply with your comments here ↓
</p>&mdash; ai (<a href="https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef?ref_src=embed">@yui.syui.ai</a>) <a href="https://bsky.app/profile/did:plc:4hqjfn7m6n5hno3doamuhgef/post/3kqxbtmwlje2h?ref_src=embed">Apr 25, 2024 at 20:18</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>
```ts
<link href="https://syui.ai/js/comment/app.js" rel="preload" as="script">
<link href="https://syui.ai/js/comment/chunk-vendors.js" rel="preload" as="script">
<div id="comment"></div>
<script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>
<script src="https://syui.ai/js/comment/chunk-vendors.js"></script>
<script src="https://syui.ai/js/comment/app.js"></script>
```
## example json
```json
[
{
"uri": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.feed.post/3kp4ze5dcek2j",
"cid": "bafyreic4g7mthhw654zgv4skt5tqbs2xqg6n7bli4gayl2nquljngnotiy",
"author": {
"did": "did:plc:wkzuqomvkxx5eiv5nl2lvm23",
"handle": "syui.syu.is",
"displayName": "syui",
"avatar": "https://api.syu.is/img/avatar/plain/did:plc:wkzuqomvkxx5eiv5nl2lvm23/bafkreifvabvstfgawt6csagh44xdevb6c2uiwpgfho3xnpdrr6o7nbkxry@jpeg",
"indexedAt": "2024-01-14T10:20:13.367Z",
"viewer": {
"muted": false,
"blockedBy": false,
"following": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.graph.follow/3kiztjatnms25",
"followedBy": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.graph.follow/3kirwsboeos26"
},
"labels": []
},
"reason": "reply",
"reasonSubject": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j",
"record": {
"text": "1",
"$type": "app.bsky.feed.post",
"langs": [
"ja"
],
"reply": {
"root": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
},
"parent": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
}
},
"createdAt": "2024-04-02T07:12:28.799Z"
},
"isRead": true,
"indexedAt": "2024-04-02T07:12:28.799Z",
"labels": []
},
{
"uri": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.feed.post/3kp54af2zes2j",
"cid": "bafyreig4kvfpu557qehttt2y5eh7rcyodbxqwtnl73f3fhjsstiap3abzu",
"author": {
"did": "did:plc:wkzuqomvkxx5eiv5nl2lvm23",
"handle": "syui.syu.is",
"displayName": "syui",
"avatar": "https://api.syu.is/img/avatar/plain/did:plc:wkzuqomvkxx5eiv5nl2lvm23/bafkreifvabvstfgawt6csagh44xdevb6c2uiwpgfho3xnpdrr6o7nbkxry@jpeg",
"indexedAt": "2024-01-14T10:20:13.367Z",
"viewer": {
"muted": false,
"blockedBy": false,
"following": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.graph.follow/3kiztjatnms25",
"followedBy": "at://did:plc:wkzuqomvkxx5eiv5nl2lvm23/app.bsky.graph.follow/3kirwsboeos26"
},
"labels": []
},
"reason": "reply",
"reasonSubject": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j",
"record": {
"text": "2",
"$type": "app.bsky.feed.post",
"langs": [
"ja"
],
"reply": {
"root": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
},
"parent": {
"cid": "bafyreiceckunxajycacn7dbuojrwb2wmurhfkleermvewwik44cn6vqo3a",
"uri": "at://did:plc:dconvttcori3mrh2wrmehvwt/app.bsky.feed.post/3kp4zdnlo5s2j"
}
},
"createdAt": "2024-04-02T08:04:03.938Z"
},
"isRead": true,
"indexedAt": "2024-04-02T08:04:03.938Z",
"labels": []
}
]
```
```json
{
"uri": "at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.feed.post/3kp5qniyzm42h",
"cid": "bafyreihmutmtf2clpgmx5l3qpu6xea6z25xrop74mltsycs5lfacm27u6e",
"author": {
"did": "did:plc:uqzpqmrjnptsxezjx4xuh2mn",
"handle": "syui.ai",
"displayName": "syui",
"avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:uqzpqmrjnptsxezjx4xuh2mn/bafkreid6kcc5pnn4b3ar7mj6vi3eiawhxgkcrw3edgbqeacyrlnlcoetea@jpeg",
"viewer": {
"muted": false,
"blockedBy": false,
"followedBy": "at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/app.bsky.graph.follow/3kkvst5iq6r2a"
},
"labels": [],
"description": "https://syui.ai",
"indexedAt": "2024-01-25T23:54:12.979Z"
},
"reason": "reply",
"reasonSubject": "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kp5qn72s232q",
"record": {
"$type": "app.bsky.feed.post",
"createdAt": "2024-04-02T14:09:18.926Z",
"langs": [
"ja"
],
"reply": {
"parent": {
"cid": "bafyreiewdfyh6rywpkdzpmf5markqa6tavc5smc32q7cw2wpwbqik5hnfm",
"uri": "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kp5qn72s232q"
},
"root": {
"cid": "bafyreiewdfyh6rywpkdzpmf5markqa6tavc5smc32q7cw2wpwbqik5hnfm",
"uri": "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.post/3kp5qn72s232q"
}
},
"text": "first"
},
"isRead": true,
"indexedAt": "2024-04-02T14:09:18.926Z",
"labels": []
}
```

BIN
icon/bot_scpt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

57
k8s/aios-deployment.yaml Normal file
View File

@@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert -f ../compose.yml --volumes hostPath
kompose.version: 1.32.0 (HEAD)
labels:
io.kompose.service: aios
name: aios
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: aios
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert -f ../compose.yml --volumes hostPath
kompose.version: 1.32.0 (HEAD)
labels:
io.kompose.network/bot-default: "true"
io.kompose.service: aios
spec:
containers:
- env:
- name: ADMIN
valueFrom:
configMapKeyRef:
key: ADMIN
name: env
- name: HANDLE
valueFrom:
configMapKeyRef:
key: HANDLE
name: env
- name: HOST
valueFrom:
configMapKeyRef:
key: HOST
name: env
- name: PASSWORD
valueFrom:
configMapKeyRef:
key: PASSWORD
name: env
image: aios
name: aios
volumeMounts:
- mountPath: /root/.config
name: aios-hostpath0
restartPolicy: Always
volumes:
- hostPath:
path: /Users/syui/ai/bot/.config
name: aios-hostpath0

11
k8s/env-configmap.yaml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: v1
data:
ADMIN: $ADMIN
HANDLE: $HANDLE
HOST: $HOST
PASSWORD: $PASSWORD
kind: ConfigMap
metadata:
labels:
io.kompose.service: aios-env
name: env

37
scripts/setup-migration.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/bin/bash
# ai.bot 移行セットアップスクリプト
echo "=== ai.bot Migration Setup ==="
# 1. 新しい設定ディレクトリの作成
echo "Creating new config directory..."
mkdir -p ~/.config/syui/ai/bot/
# 2. スクリプトディレクトリの移動gitサブモジュール
if [ -d ~/.config/ai/scpt ]; then
echo "Copying script directory..."
cp -r ~/.config/ai/scpt ~/.config/syui/ai/bot/
echo "Scripts copied to ~/.config/syui/ai/bot/scpt/"
fi
# 3. 設定ファイルの移行自動的にdata.rsで行われる
echo "Configuration files will be migrated automatically when used."
# 4. エイリアス設定の提案
echo ""
echo "=== Manual Steps Required ==="
echo ""
echo "1. Add this alias to your shell profile (~/.zshrc, ~/.bashrc, etc.):"
echo " alias ai='aibot'"
echo ""
echo "2. Install the new binaries:"
echo " cargo install --path ."
echo ""
echo "3. Or add to PATH:"
echo " export PATH=\"$(pwd)/target/debug:\$PATH\""
echo ""
echo "4. Update git submodule path if needed:"
echo " cd ~/.config/syui/ai/bot/scpt"
echo " git remote -v # Check current remote"
echo ""
echo "Migration setup complete!"

39
src/alias.rs Normal file
View File

@@ -0,0 +1,39 @@
// Legacy alias: ai -> aibot
// This provides backward compatibility for existing scripts
use std::env;
use std::path::PathBuf;
use std::process::{Command, exit};
fn main() {
let args: Vec<String> = env::args().collect();
// Skip the first argument (program name) and pass the rest to aibot
let aibot_args: Vec<&str> = args.iter().skip(1).map(|s| s.as_str()).collect();
// Try to find aibot binary in the same directory as this binary
let current_exe = env::current_exe().unwrap_or_else(|_| PathBuf::from("ai"));
let mut aibot_path = current_exe.parent().unwrap_or(&PathBuf::from(".")).to_path_buf();
aibot_path.push("aibot");
// First try relative path, then try PATH
let mut cmd = if aibot_path.exists() {
Command::new(&aibot_path)
} else {
Command::new("aibot")
};
cmd.args(&aibot_args);
match cmd.status() {
Ok(status) => {
exit(status.code().unwrap_or(1));
}
Err(e) => {
eprintln!("Error executing aibot: {}", e);
eprintln!("Make sure 'aibot' is installed and available in PATH");
eprintln!("Attempted path: {:?}", aibot_path);
exit(1);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,36 +5,69 @@ 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;
/// 設定ディレクトリのベースパス
const CONFIG_BASE_DIR: &str = "~/.config/syui/ai/bot";
/// ホームディレクトリパスを展開するユーティリティ関数
/// "~"で始まるパスをユーザーのホームディレクトリに展開します
fn expand_home_path(path: &str) -> PathBuf {
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..]);
PathBuf::from(home).join(path_without_tilde)
} else {
PathBuf::from(path)
}
}
/// 設定ディレクトリのベースパスを取得し、必要に応じて作成する
fn get_config_base_path() -> PathBuf {
let path = expand_home_path(CONFIG_BASE_DIR);
if !path.is_dir() {
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)
};
if !path.is_dir() {
let _ = fs::create_dir_all(&path);
}
path
}
pub fn data_file(s: &str) -> String { pub fn data_file(s: &str) -> String {
let file = "/.config/ai/"; let path = get_config_base_path();
let mut f = shellexpand::tilde("~").to_string(); let path_str = path.to_string_lossy();
f.push_str(&file);
let path = Path::new(&f); match s {
if path.is_dir() == false { "toml" => format!("{}/token.toml", path_str),
let _ = fs::create_dir_all(f.clone()); "json" => format!("{}/token.json", path_str),
} "refresh" => format!("{}/refresh.toml", path_str),
match &*s { _ => format!("{}/.{}", path_str, s),
"toml" => f + &"token.toml",
"json" => f + &"token.json",
"refresh" => f + &"refresh.toml",
_ => f + &"." + &s,
} }
} }
pub fn log_file(s: &str) -> String { pub fn log_file(s: &str) -> String {
let file = "/.config/ai/txt/"; let path = get_config_path("txt");
let mut f = shellexpand::tilde("~").to_string(); let path_str = path.to_string_lossy();
f.push_str(&file);
let path = Path::new(&f); match s {
if path.is_dir() == false { "n1" => format!("{}/notify_cid.txt", path_str),
let _ = fs::create_dir_all(f.clone()); "n2" => format!("{}/notify_cid_run.txt", path_str),
} "c1" => format!("{}/comment_cid.txt", path_str),
match &*s { _ => format!("{}/{}", path_str, s),
"n1" => f + &"notify_cid.txt",
"n2" => f + &"notify_cid_run.txt",
_ => f + &s,
} }
} }
@@ -84,6 +117,7 @@ pub struct Token {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct Data { pub struct Data {
pub host: String, pub host: String,
pub password: String,
pub did: String, pub did: String,
pub handle: String, pub handle: String,
pub access: String, pub access: String,
@@ -105,6 +139,7 @@ pub struct BaseUrl {
pub record_list: String, pub record_list: String,
pub record_create: String, pub record_create: String,
pub record_delete: String, pub record_delete: String,
pub record_put: String,
pub session_create: String, pub session_create: String,
pub session_refresh: String, pub session_refresh: String,
pub session_get: String, pub session_get: String,
@@ -122,22 +157,24 @@ pub struct BaseUrl {
pub follow: String, pub follow: String,
pub follows: String, pub follows: String,
pub followers: String, pub followers: String,
pub feed_get: String,
} }
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",
handle: data.handle, get_config_base_path().display());
did: data.did, eprintln!("Please run 'aibot login <handle> -p <password>' first to authenticate.");
access: data.access, std::process::exit(1);
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 {
profile_get: "com.atproto.identity.resolveHandle".to_string(), profile_get: "com.atproto.identity.resolveHandle".to_string(),
thread_get: "app.bsky.feed.getPostThread".to_string(), thread_get: "app.bsky.feed.getPostThread".to_string(),
record_put: "com.atproto.repo.putRecord".to_string(),
record_create: "com.atproto.repo.createRecord".to_string(), record_create: "com.atproto.repo.createRecord".to_string(),
record_delete: "com.atproto.repo.deleteRecord".to_string(), record_delete: "com.atproto.repo.deleteRecord".to_string(),
describe: "com.atproto.repo.describeRepo".to_string(), describe: "com.atproto.repo.describeRepo".to_string(),
@@ -146,6 +183,7 @@ pub fn url(s: &str) -> String {
session_refresh: "com.atproto.server.refreshSession".to_string(), session_refresh: "com.atproto.server.refreshSession".to_string(),
session_get: "com.atproto.server.getSession".to_string(), session_get: "com.atproto.server.getSession".to_string(),
timeline_get: "app.bsky.feed.getTimeline".to_string(), timeline_get: "app.bsky.feed.getTimeline".to_string(),
feed_get: "app.bsky.feed.getFeed".to_string(),
timeline_author: "app.bsky.feed.getAuthorFeed".to_string(), timeline_author: "app.bsky.feed.getAuthorFeed".to_string(),
like: "app.bsky.feed.like".to_string(), like: "app.bsky.feed.like".to_string(),
repost: "app.bsky.feed.repost".to_string(), repost: "app.bsky.feed.repost".to_string(),
@@ -168,6 +206,7 @@ pub fn url(s: &str) -> String {
"record_list" => t.to_string() + &baseurl.record_list, "record_list" => t.to_string() + &baseurl.record_list,
"record_create" => t.to_string() + &baseurl.record_create, "record_create" => t.to_string() + &baseurl.record_create,
"record_delete" => t.to_string() + &baseurl.record_delete, "record_delete" => t.to_string() + &baseurl.record_delete,
"record_put" => t.to_string() + &baseurl.record_put,
"session_create" => t.to_string() + &baseurl.session_create, "session_create" => t.to_string() + &baseurl.session_create,
"session_refresh" => t.to_string() + &baseurl.session_refresh, "session_refresh" => t.to_string() + &baseurl.session_refresh,
"session_get" => t.to_string() + &baseurl.session_get, "session_get" => t.to_string() + &baseurl.session_get,
@@ -185,50 +224,80 @@ pub fn url(s: &str) -> String {
"follow" => t.to_string() + &baseurl.follow, "follow" => t.to_string() + &baseurl.follow,
"follows" => t.to_string() + &baseurl.follows, "follows" => t.to_string() + &baseurl.follows,
"followers" => t.to_string() + &baseurl.followers, "followers" => t.to_string() + &baseurl.followers,
_ => s, "feed_get" => t.to_string() + &baseurl.feed_get,
_ => 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",
handle: data.handle, get_config_base_path().display());
did: data.did, eprintln!("Please run 'aibot login <handle> -p <password>' first to authenticate.");
access: data.access, std::process::exit(1);
refresh: data.refresh, }
}; };
match &*s {
"host" => data.handle, match s {
"host" => data.host,
"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(),
} }
} }
pub fn data_refresh(s: &str) -> String { pub fn c_refresh(access: &str, refresh: &str) {
let s = String::from(s); let ff = data_file(&"refresh");
let refresh = Refresh::new().unwrap(); let mut ff = fs::File::create(ff.clone()).unwrap();
let refresh = Refresh { let refreshs = Refresh {
access: refresh.access, access: access.to_string(),
refresh: refresh.refresh, refresh: refresh.to_string(),
}; };
match &*s { let toml = toml::to_string(&refreshs).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
}
pub fn data_refresh(s: &str) -> String {
let data = match Data::new() {
Ok(data) => data,
Err(_) => {
eprintln!("Error: Configuration file not found at {}/token.toml",
get_config_base_path().display());
eprintln!("Please run 'aibot login <handle> -p <password>' first to authenticate.");
std::process::exit(1);
}
};
let mut _file = match Refresh::new() {
Err(_why) => c_refresh(&data.access, &data.refresh),
Ok(_) => println!(""),
};
let refresh = match Refresh::new() {
Ok(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 {
"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)]
@@ -490,15 +559,29 @@ pub fn c_char(i: String) -> String {
return s; return s;
} }
pub fn w_cfg(h: &str, res: &str) { pub fn w_cfg(h: &str, res: &str, password: &str) {
let f = data_file(&"json"); let f = data_file(&"json");
let ff = data_file(&"toml"); let ff = data_file(&"toml");
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(),
did: json.did.to_string(), did: json.did.to_string(),
handle: json.handle.to_string(), handle: json.handle.to_string(),
access: json.accessJwt.to_string(), access: json.accessJwt.to_string(),
@@ -506,14 +589,20 @@ pub fn w_cfg(h: &str, res: &str) {
}; };
let toml = toml::to_string(&datas).unwrap(); let toml = toml::to_string(&datas).unwrap();
ff.write_all(&toml.as_bytes()).unwrap(); ff.write_all(&toml.as_bytes()).unwrap();
let ff = data_file(&"refresh");
let mut ff = fs::File::create(ff.clone()).unwrap();
let refreshs = Refresh {
access: json.accessJwt.to_string(),
refresh: json.refreshJwt.to_string(),
};
let toml = toml::to_string(&refreshs).unwrap();
ff.write_all(&toml.as_bytes()).unwrap();
} }
pub fn w_refresh(res: &str) { pub fn w_refresh(res: &str) {
let f = data_file(&"json");
let ff = data_file(&"refresh"); let ff = data_file(&"refresh");
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();
let json: Token = serde_json::from_str(&res).unwrap(); let json: Token = serde_json::from_str(&res).unwrap();
let refreshs = Refresh { let refreshs = Refresh {
access: json.accessJwt.to_string(), access: json.accessJwt.to_string(),
@@ -558,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);
@@ -572,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();
} }
@@ -583,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()

21
src/delete_record.rs Normal file
View File

@@ -0,0 +1,21 @@
use crate::http_client::HttpClient;
use crate::data_toml;
use crate::url;
use serde_json::json;
pub async fn post_request(rkey: String, col: String) -> String {
let handle = data_toml(&"handle");
let url = url(&"record_delete");
let client = HttpClient::new();
let post = json!({
"repo": handle.to_string(),
"rkey": rkey.to_string(),
"collection": col.to_string()
});
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
}

View File

@@ -1,22 +1,13 @@
extern crate reqwest; use crate::http_client::HttpClient;
//use crate::data_toml;
use crate::url; use crate::url;
pub async fn get_request(user: String) -> String { pub async fn get_request(user: String) -> String {
//let token = data_refresh(&"access"); let base_url = url(&"describe");
let url = url(&"describe"); let url = format!("{}?repo={}", base_url, user);
let client = HttpClient::new();
let client = reqwest::Client::new(); match client.get(&url).await {
let res = client Ok(response) => response,
.get(url) Err(e) => format!("Error: {}", e),
.query(&[("repo", &user)]) }
//.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
} }

13
src/feed_get.rs Normal file
View File

@@ -0,0 +1,13 @@
use crate::http_client::HttpClient;
use crate::url;
pub async fn get_request(feed: String) -> String {
let base_url = url(&"feed_get");
let url = format!("{}?feed={}", base_url, feed);
let client = HttpClient::new();
match client.get_with_auth(&url).await {
Ok(response) => response,
Err(_) => "err".to_string(),
}
}

77
src/feed_watch.rs Normal file
View File

@@ -0,0 +1,77 @@
use seahorse::Context;
//use crate::openai;
use crate::feed_get;
use crate::data::data_toml;
use crate::data::Timeline;
use crate::data::log_file;
use crate::data::w_cid;
pub fn c_feed_watch(c: &Context) {
let mut feed = "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd".to_string();
if c.string_flag("url").is_ok() {
feed = c.string_flag("url").unwrap();
}
let mut tag = "syai".to_string();
if c.string_flag("tag").is_ok() {
tag = c.string_flag("tag").unwrap();
}
let h = async {
let notify = feed_get::get_request(feed).await;
if notify == "err" {
return;
//refresh(c);
//notify = feed_get::get_request("at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd".to_string()).await;
}
let timeline: Timeline = serde_json::from_str(&notify).unwrap();
let n = timeline.feed;
let host = data_toml(&"host");
let length = &n.len();
let su = 0..*length;
for i in su {
let cid = &n[i].post.cid;
let check_cid = w_cid(cid.to_string(), log_file(&"n1"), false);
let handle = &n[i].post.author.handle;
let did = &n[i].post.author.did;
let uri = &n[i].post.uri;
let _time = &n[i].post.indexedAt;
let cid_root = cid;
let uri_root = uri;
let mut text = "";
if !n[i].post.record.text.is_none() {
text = &n[i].post.record.text.as_ref().unwrap();
}
let vec: Vec<&str> = text.split_whitespace().collect();
let com = vec[0].trim().to_string();
let mut prompt = "".to_string();
let mut prompt_sub = "".to_string();
if com == "@ai" || com == "/ai" || com == tag {
prompt_sub = vec[1..].join(" ");
} else {
prompt = vec[1..].join(" ");
if vec.len() > 1 {
prompt_sub = vec[2..].join(" ");
}
}
if check_cid == false && { prompt.is_empty() == false || com.is_empty() == false } {
println!("{}", handle);
if c.bool_flag("debug") == true {
println!(
"cid:{}\nuri:{}\ncid_root:{}\nuri_root:{}\nhost:{}\ndid:{}\ncheck_cid:{}",
cid, uri, cid_root, uri_root, host, did, check_cid
);
}
println!("{}", prompt_sub);
println!("---");
w_cid(cid.to_string(), log_file(&"n1"), true);
}
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res;
}

View File

@@ -1,14 +1,12 @@
extern crate reqwest;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use crate::http_client::HttpClient;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use serde_json::json; use serde_json::json;
//use crate::data::Follow; //use crate::data::Follow;
pub async fn post_request(u: String) -> String { pub async fn post_request(u: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
@@ -18,7 +16,7 @@ pub async fn post_request(u: String) -> String {
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -26,25 +24,19 @@ pub async fn post_request(u: String) -> String {
"subject": u.to_string(), "subject": u.to_string(),
"createdAt": d.to_string(), "createdAt": d.to_string(),
}, },
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client match client.post_json_with_auth(&url, &post).await {
.post(url) Ok(response) => response,
.json(&post) Err(e) => {
.header("Authorization", "Bearer ".to_owned() + &token) eprintln!("Error following user: {}", e);
.send() "err".to_string()
.await }
.unwrap() }
.text()
.await
.unwrap();
return res;
} }
pub async fn delete_request(u: String, rkey: String) -> String { pub async fn delete_request(u: String, rkey: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
@@ -54,7 +46,7 @@ pub async fn delete_request(u: String, rkey: String) -> String {
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -63,19 +55,14 @@ pub async fn delete_request(u: String, rkey: String) -> String {
"subject": u.to_string(), "subject": u.to_string(),
"createdAt": d.to_string(), "createdAt": d.to_string(),
}, },
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client match client.post_json_with_auth(&url, &post).await {
.post(url) Ok(response) => response,
.json(&post) Err(e) => {
.header("Authorization", "Bearer ".to_owned() + &token) eprintln!("Error unfollowing user: {}", e);
.send() "err".to_string()
.await }
.unwrap() }
.text()
.await
.unwrap();
return res;
} }

View File

@@ -1,24 +1,14 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_refresh;
use crate::url; use crate::url;
//use serde_json::json;
pub async fn get_request(actor: String, cursor: Option<String>) -> String { pub async fn get_request(actor: String, cursor: Option<String>) -> String {
let token = data_refresh(&"access"); let base_url = url(&"followers");
let url = url(&"followers");
let cursor = cursor.unwrap(); let cursor = cursor.unwrap();
let url = format!("{}?actor={}&cursor={}", base_url, actor, cursor);
let client = HttpClient::new();
let client = reqwest::Client::new(); match client.get_with_auth(&url).await {
let res = client Ok(response) => response,
.get(url) Err(e) => format!("Error: {}", e),
.query(&[("actor", actor), ("cursor", cursor)]) }
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
} }

View File

@@ -1,26 +1,14 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_refresh;
use crate::url; use crate::url;
//use serde_json::json;
pub async fn get_request(actor: String, cursor: Option<String>) -> String { pub async fn get_request(actor: String, cursor: Option<String>) -> String {
let token = data_refresh(&"access"); let base_url = url(&"follows");
let url = url(&"follows");
let cursor = cursor.unwrap(); let cursor = cursor.unwrap();
//let cursor = "1682386039125::bafyreihwgwozmvqxcxrhbr65agcaa4v357p27ccrhzkjf3mz5xiozjvzfa".to_string(); let url = format!("{}?actor={}&cursor={}", base_url, actor, cursor);
//let cursor = "1682385956974::bafyreihivhux5m3sxbg33yruhw5ozhahwspnuqdsysbo57smzgptdcluem".to_string(); let client = HttpClient::new();
let client = reqwest::Client::new(); match client.get_with_auth(&url).await {
let res = client Ok(response) => response,
.get(url) Err(e) => format!("Error: {}", e),
.query(&[("actor", actor), ("cursor", cursor)]) }
//cursor.unwrap()
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
} }

5
src/game.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod post_card;
pub mod post_card_verify;
pub mod post_game;
pub mod post_game_user;
pub mod post_game_login;

35
src/game/post_card.rs Normal file
View File

@@ -0,0 +1,35 @@
use crate::http_client::HttpClient;
use crate::data_toml;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(verify: String, id: i32, cp: i32, rank: i32, rare: String, col: String, author: String) -> String {
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"id": id,
"cp": cp,
"rank": rank,
"rare": rare.to_string(),
"author": author.to_string(),
"verify": verify.to_string(),
"createdAt": d.to_string(),
},
});
let client = HttpClient::new();
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
}

View File

@@ -0,0 +1,49 @@
use crate::http_client::HttpClient;
use crate::data_toml;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, img: String, id: i32, cp: i32, rank: i32, rare: String, user_handle: String, user_did: String) -> String {
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_create");
let d = Timestamp::now_utc();
let d = d.to_string();
let link = "https://bsky.app/profile/yui.syui.ai".to_string();
let post = json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"record": {
"id": id,
"cp": cp,
"rank": rank,
"rare": rare.to_string(),
"handle": user_handle.to_string(),
"did": user_did.to_string(),
"embed": {
"$type": "app.bsky.embed.external",
"external": {
"uri": link,
"thumb": {
"$type": "blob",
"ref": {
"$link": img.to_string()
},
"mimeType": "image/jpeg",
"size": 0
}
}
},
"createdAt": d.to_string(),
},
});
let client = HttpClient::new();
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
}

30
src/game/post_game.rs Normal file
View File

@@ -0,0 +1,30 @@
use crate::http_client::HttpClient;
use crate::data_toml;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, account: String) -> String {
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_put");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": "self".to_string(),
"record": {
"account": account.to_string(),
"createdAt": d.to_string(),
},
});
let client = HttpClient::new();
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
}

View File

@@ -0,0 +1,33 @@
use crate::http_client::HttpClient;
use crate::data_toml;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, username: String, login: bool, account: String) -> String {
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_put");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": "self".to_string(),
"record": {
"login": login,
"username": username.to_string(),
"account": account.to_string(),
"createdAt": d.to_string(),
},
});
let client = HttpClient::new();
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
}

View File

@@ -0,0 +1,46 @@
use crate::http_client::HttpClient;
use crate::data_toml;
use crate::url;
use iso8601_timestamp::Timestamp;
use serde_json::json;
pub async fn post_request(col: String, user_name: String, user_did: String, user_handle: String, aiten: i32, limit: i32, chara: String, lv: i32, exp: i32, hp: i32, rank: i32, mode: i32, attach: i32, critical: i32, critical_d: i32) -> String {
let did = data_toml(&"did");
let handle = data_toml(&"handle");
let url = url(&"record_put");
let d = Timestamp::now_utc();
let d = d.to_string();
let post = json!({
"repo": handle.to_string(),
"did": did.to_string(),
"collection": col.to_string(),
"rkey": user_name.to_string(),
"record": {
"did": user_did.to_string(),
"handle": user_handle.to_string(),
"aiten": aiten,
"limit": limit,
"character": {
chara.to_string(): {
"lv": lv,
"exp": exp,
"hp": hp,
"rank": rank,
"mode": mode,
"attach": attach,
"critical": critical,
"critical_d": critical_d,
}
},
"createdAt": d.to_string(),
"updatedAt": d.to_string(),
},
});
let client = HttpClient::new();
match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
}

114
src/http_client.rs Normal file
View File

@@ -0,0 +1,114 @@
use reqwest::{Client, Error};
use serde::Serialize;
use crate::data_refresh;
pub struct HttpClient {
client: Client,
}
impl HttpClient {
pub fn new() -> Self {
Self {
client: Client::new(),
}
}
/// GET request with authentication
pub async fn get_with_auth(&self, url: &str) -> Result<String, Error> {
let token = data_refresh(&"access");
let response = self.client
.get(url)
.header("Authorization", format!("Bearer {}", token))
.send()
.await?
.text()
.await?;
Ok(response)
}
/// POST request with JSON body and authentication
pub async fn post_json_with_auth<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error> {
let token = data_refresh(&"access");
let response = self.client
.post(url)
.json(json)
.header("Authorization", format!("Bearer {}", token))
.send()
.await?
.text()
.await?;
Ok(response)
}
/// DELETE request with authentication
pub async fn delete_with_auth(&self, url: &str) -> Result<String, Error> {
let token = data_refresh(&"access");
let response = self.client
.delete(url)
.header("Authorization", format!("Bearer {}", token))
.send()
.await?
.text()
.await?;
Ok(response)
}
/// POST request without authentication (for login, etc.)
pub async fn post_json<T: Serialize>(&self, url: &str, json: &T) -> Result<String, Error> {
let response = self.client
.post(url)
.json(json)
.send()
.await?
.text()
.await?;
Ok(response)
}
/// GET request without authentication
pub async fn get(&self, url: &str) -> Result<String, Error> {
let response = self.client
.get(url)
.send()
.await?
.text()
.await?;
Ok(response)
}
/// POST request with custom headers
pub async fn post_with_headers<T: Serialize>(
&self,
url: &str,
json: &T,
headers: Vec<(&str, &str)>
) -> Result<String, Error> {
let mut request = self.client.post(url).json(json);
for (key, value) in headers {
request = request.header(key, value);
}
let response = request
.send()
.await?
.text()
.await?;
Ok(response)
}
}
impl Default for HttpClient {
fn default() -> Self {
Self::new()
}
}

View File

@@ -1,23 +1,19 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use serde_json::json; use serde_json::json;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
pub async fn post_request(text: String, link: String) -> String { pub async fn post_request(text: String, link: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
let url = url(&"record_create"); let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string(); let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -41,19 +37,12 @@ pub async fn post_request(text: String, link: String) -> String {
] ]
} }
} }
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
} }

View File

@@ -1,6 +1,5 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use serde_json::json; use serde_json::json;
@@ -12,17 +11,15 @@ pub async fn post_request(
uri: String, uri: String,
itype: String, itype: String,
) -> String { ) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
let url = url(&"record_create"); let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string(); let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -56,19 +53,12 @@ pub async fn post_request(
} }
} }
} }
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res; match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
} }

View File

@@ -1,23 +1,19 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use serde_json::json; use serde_json::json;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
pub async fn post_request(text: String, link: String) -> String { pub async fn post_request(text: String, link: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
let url = url(&"record_create"); let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string(); let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -41,19 +37,12 @@ pub async fn post_request(text: String, link: String) -> String {
] ]
} }
} }
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
} }

View File

@@ -1,12 +1,10 @@
extern crate reqwest;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use crate::http_client::HttpClient;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use serde_json::json; use serde_json::json;
pub async fn post_request(cid: String, uri: String) -> String { pub async fn post_request(cid: String, uri: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
@@ -16,7 +14,7 @@ pub async fn post_request(cid: String, uri: String) -> String {
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -27,19 +25,14 @@ pub async fn post_request(cid: String, uri: String) -> String {
}, },
"createdAt": d.to_string(), "createdAt": d.to_string(),
}, },
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client match client.post_json_with_auth(&url, &post).await {
.post(url) Ok(response) => response,
.json(&post) Err(e) => {
.header("Authorization", "Bearer ".to_owned() + &token) eprintln!("Error liking post: {}", e);
.send() "err".to_string()
.await }
.unwrap() }
.text()
.await
.unwrap();
return res;
} }

View File

@@ -3,6 +3,7 @@ use std::env;
use crate::ascii::c_ascii; use crate::ascii::c_ascii;
use crate::bot::c_bot; use crate::bot::c_bot;
use crate::bot::c_bot_feed;
use crate::data::c_follow_all; use crate::data::c_follow_all;
use crate::data::c_openai_key; use crate::data::c_openai_key;
use crate::data::data_toml; use crate::data::data_toml;
@@ -10,6 +11,12 @@ use crate::data::data_refresh;
use crate::data::url; use crate::data::url;
use crate::data::w_cfg; use crate::data::w_cfg;
use crate::data::w_refresh; use crate::data::w_refresh;
use crate::feed_watch::c_feed_watch;
use crate::game::post_card;
use crate::game::post_card_verify;
use crate::game::post_game;
use crate::game::post_game_user;
use crate::game::post_game_login;
use data::ProfileIdentityResolve; use data::ProfileIdentityResolve;
@@ -20,6 +27,7 @@ pub mod describe;
pub mod follow; pub mod follow;
pub mod followers; pub mod followers;
pub mod follows; pub mod follows;
pub mod http_client;
pub mod img_reply; pub mod img_reply;
pub mod like; pub mod like;
pub mod mention; pub mod mention;
@@ -28,6 +36,7 @@ pub mod notify_read;
pub mod openai; pub mod openai;
pub mod post; pub mod post;
pub mod post_link; pub mod post_link;
pub mod game;
pub mod profile; pub mod profile;
pub mod refresh; pub mod refresh;
pub mod reply; pub mod reply;
@@ -37,10 +46,20 @@ pub mod repost;
pub mod session; pub mod session;
pub mod timeline_author; pub mod timeline_author;
pub mod token; pub mod token;
pub mod feed_get;
pub mod feed_watch;
pub mod delete_record;
pub mod update_handle;
#[cfg(test)]
mod tests;
fn main() { fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let app = App::new(env!("CARGO_PKG_NAME")) let app = App::new(env!("CARGO_PKG_NAME"))
.author(env!("CARGO_PKG_AUTHORS"))
.version(env!("CARGO_PKG_VERSION"))
.description(env!("CARGO_PKG_DESCRIPTION"))
.command( .command(
Command::new("ai") Command::new("ai")
.alias("a") .alias("a")
@@ -59,6 +78,26 @@ fn main() {
Flag::new("admin", FlagType::String) Flag::new("admin", FlagType::String)
.alias("a"), .alias("a"),
) )
.flag(
Flag::new("feed", FlagType::String)
.alias("f"),
)
)
.command(
Command::new("feed_watch")
.action(feed_watch)
.flag(
Flag::new("url", FlagType::String)
.alias("u"),
)
.flag(
Flag::new("tag", FlagType::String)
.alias("t"),
)
.flag(
Flag::new("debug", FlagType::Bool)
.alias("d"),
)
) )
.command( .command(
Command::new("follow_all") Command::new("follow_all")
@@ -67,7 +106,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)
@@ -79,6 +118,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")
@@ -95,6 +139,12 @@ fn main() {
.alias("t") .alias("t")
.action(timeline), .action(timeline),
) )
.command(
Command::new("feed")
.description("feed <feed-uri>")
.alias("f")
.action(feed)
)
.command( .command(
Command::new("did") Command::new("did")
.description("did <handle>") .description("did <handle>")
@@ -110,6 +160,175 @@ fn main() {
.alias("l"), .alias("l"),
) )
) )
.command(
Command::new("delete")
.description("d <rkey> -c <collection>")
.alias("d")
.action(delete)
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
)
.command(
Command::new("update-handle")
.description("update-handle <new_handle>")
.action(update_handle)
)
.command(
Command::new("card")
.description("-v <at://verify> -i <int:id> -p <int:cp> -r <int:rank> -c <collection> -a <author> -img <link> -rare <normal>")
.action(card)
.flag(
Flag::new("id", FlagType::Int)
.alias("i"),
)
.flag(
Flag::new("cp", FlagType::Int)
.alias("p"),
)
.flag(
Flag::new("rank", FlagType::Int)
.alias("r"),
)
.flag(
Flag::new("rare", FlagType::Int)
)
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("author", FlagType::String)
.alias("a"),
)
.flag(
Flag::new("verify", FlagType::String)
.alias("v"),
)
.flag(
Flag::new("img", FlagType::String)
)
)
.command(
Command::new("card-verify")
.description("<at://verify> -c <collection> -i <id> -p <cp> -r <rank> -rare <normal> -H <syui.ai> -d <did>")
.action(card_verify)
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("id", FlagType::Int)
.alias("i"),
)
.flag(
Flag::new("cp", FlagType::Int)
.alias("p"),
)
.flag(
Flag::new("rank", FlagType::Int)
.alias("r"),
)
.flag(
Flag::new("rare", FlagType::String)
)
.flag(
Flag::new("handle", FlagType::String)
.alias("H"),
)
.flag(
Flag::new("did", FlagType::String)
.alias("did"),
)
)
.command(
Command::new("game")
.description("a <at://yui.syui.ai/ai.syui.game.user/username>")
.action(game)
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("account", FlagType::String)
.alias("a"),
)
)
.command(
Command::new("game-login")
.description("l <bool> -u <username> -c <collection>")
.action(game_login)
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("login", FlagType::Bool)
.alias("l"),
)
.flag(
Flag::new("username", FlagType::String)
.alias("u"),
)
)
.command(
Command::new("game-user")
.description("-chara ai -l 20240101 -ten 0 --lv 0 --exp 0 --hp 0 --rank 0 --mode 0 --attach 0 --critical 0 --critical_d 0")
.action(game_user)
.flag(
Flag::new("username", FlagType::String)
.alias("u"),
)
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
.flag(
Flag::new("did", FlagType::String)
.alias("d"),
)
.flag(
Flag::new("handle", FlagType::String)
.alias("H"),
)
.flag(
Flag::new("character", FlagType::String)
.alias("chara"),
)
.flag(
Flag::new("aiten", FlagType::Int)
.alias("ten"),
)
.flag(
Flag::new("limit", FlagType::Int)
.alias("l"),
)
.flag(
Flag::new("lv", FlagType::Int)
)
.flag(
Flag::new("hp", FlagType::Int)
)
.flag(
Flag::new("attach", FlagType::Int)
)
.flag(
Flag::new("exp", FlagType::Int)
)
.flag(
Flag::new("critical", FlagType::Int)
)
.flag(
Flag::new("critical_d", FlagType::Int)
)
.flag(
Flag::new("rank", FlagType::Int)
)
.flag(
Flag::new("mode", FlagType::Int)
)
)
.command( .command(
Command::new("like") Command::new("like")
.description("like <cid> -u <uri>") .description("like <cid> -u <uri>")
@@ -140,6 +359,12 @@ fn main() {
Flag::new("cid", FlagType::String) Flag::new("cid", FlagType::String)
.alias("c"), .alias("c"),
) )
.flag(
Flag::new("uri-root", FlagType::String)
)
.flag(
Flag::new("cid-root", FlagType::String)
)
.flag( .flag(
Flag::new("link", FlagType::String) Flag::new("link", FlagType::String)
.alias("l"), .alias("l"),
@@ -184,6 +409,10 @@ fn main() {
Flag::new("post", FlagType::String) Flag::new("post", FlagType::String)
.alias("p"), .alias("p"),
) )
.flag(
Flag::new("col", FlagType::String)
.alias("c"),
)
) )
.command( .command(
Command::new("follow") Command::new("follow")
@@ -265,6 +494,14 @@ fn bot(c: &Context) {
refresh(c); refresh(c);
loop { loop {
c_bot(c); c_bot(c);
c_bot_feed(c);
}
}
fn feed_watch(c: &Context) {
refresh(c);
loop {
c_feed_watch(c);
} }
} }
@@ -277,20 +514,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);
w_refresh(&res); let res = token::post_request(m.to_string(), p.to_string(), server.to_string(), code).await;
} else { w_cfg(&server, &res, &p);
let res =
token::post_request(m.to_string(), p.to_string(), "bsky.social".to_string())
.await;
w_cfg(&"bsky.social", &res);
w_refresh(&res);
}
} }
}; };
let res = tokio::runtime::Runtime::new().unwrap().block_on(h); let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
@@ -302,8 +538,15 @@ fn refresh(_c: &Context) {
let session = session::get_request().await; let session = session::get_request().await;
if session == "err" { if session == "err" {
let res = refresh::post_request().await; let res = refresh::post_request().await;
println!("{}", res); if res == "err" {
w_refresh(&res) let m = data_toml(&"handle");
let p = data_toml(&"password");
let s = data_toml(&"host");
let res = token::post_request(m.to_string(), p.to_string(), s.to_string(), None).await;
w_cfg(&s, &res, &p);
} else {
w_refresh(&res);
}
} }
}; };
let res = tokio::runtime::Runtime::new().unwrap().block_on(h); let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
@@ -320,6 +563,22 @@ fn notify(c: &Context) {
return res; return res;
} }
fn feed(c: &Context) {
refresh(c);
let feed_d = "at://did:plc:4hqjfn7m6n5hno3doamuhgef/app.bsky.feed.generator/cmd".to_string();
let h = async {
if c.args.len() == 0 {
let j = feed_get::get_request(feed_d).await;
println!("{}", j);
} else {
let j = feed_get::get_request(c.args[0].to_string()).await;
println!("{}", j);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res;
}
fn did(c: &Context) { fn did(c: &Context) {
refresh(c); refresh(c);
let h = async { let h = async {
@@ -352,6 +611,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") {
@@ -369,8 +633,47 @@ fn post(c: &Context) {
return res; return res;
} }
fn delete(c: &Context) {
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 h = async {
if let Ok(col) = c.string_flag("col") {
let str = delete_record::post_request(m.to_string(), col);
println!("{}", str.await);
}
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res;
}
fn update_handle(c: &Context) {
refresh(c);
if c.args.is_empty() {
eprintln!("Error: New handle is required.");
eprintln!("Usage: aibot update-handle <new_handle>");
std::process::exit(1);
}
let new_handle = c.args[0].to_string();
let h = async {
let str = update_handle::post_request(new_handle);
println!("{}", str.await);
};
let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
return res;
}
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") {
@@ -382,8 +685,152 @@ fn like(c: &Context) {
return res; return res;
} }
async fn c_card(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
//let m = c.args[0].to_string();
let author = c.string_flag("author").unwrap_or_else(|_| "syui".to_string());
let verify = c.string_flag("verify").unwrap_or_else(|_| "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.card.verify/3lagpvhppmd2q".to_string());
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.card".to_string());
//let img = c.string_flag("img").unwrap_or_else(|_| "bafkreigvcjc46qtelpc4wsg7fwf6qktbi6a23ouqiupth2r37zhrn7wbza".to_string());
let id = c.int_flag("id")?.try_into()?;
let cp = c.int_flag("cp")?.try_into()?;
let rank = c.int_flag("rank")?.try_into()?;
let rare = c.string_flag("rare").unwrap_or_else(|_| "normal".to_string());
let str = post_card::post_request(verify, id, cp, rank, rare, col, author);
println!("{}", str.await);
Ok(())
}
fn card(c: &Context) {
refresh(c);
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async {
if let Err(e) = c_card(c).await {
eprintln!("Error: {}", e);
}
});
}
async fn c_card_verify(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.card.verify".to_string());
let img = c.string_flag("img").unwrap_or_else(|_| "bafkreigvcjc46qtelpc4wsg7fwf6qktbi6a23ouqiupth2r37zhrn7wbza".to_string());
let id = c.int_flag("id")?.try_into()?;
let cp = c.int_flag("cp")?.try_into()?;
let rank = c.int_flag("rank")?.try_into()?;
let rare = c.string_flag("rare").unwrap_or_else(|_| "normal".to_string());
let user_handle = c.string_flag("handle").unwrap_or_else(|_| "syui.ai".to_string());
let user_did = c.string_flag("did").unwrap_or_else(|_| "did:plc:uqzpqmrjnptsxezjx4xuh2mn".to_string());
//match id === 1 let img = "xxx";
let str = post_card_verify::post_request(col, img, id, cp, rank, rare, user_handle, user_did);
println!("{}", str.await);
Ok(())
}
fn card_verify(c: &Context) {
refresh(c);
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async {
if let Err(e) = c_card_verify(c).await {
eprintln!("Error: {}", e);
}
});
}
async fn c_game(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
let account = c.string_flag("account").unwrap_or_else(|_| "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/syui".to_string());
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game".to_string());
let handle = data_toml(&"handle");
if handle == "syui.ai" {
let str = post_game::post_request(col, account);
println!("{}", str.await);
Ok(())
} else {
Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized")))
}
}
fn game(c: &Context) {
refresh(c);
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async {
if let Err(e) = c_game(c).await {
eprintln!("Error: {}", e);
}
});
}
async fn c_game_user(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game.user".to_string());
let user_name = c.string_flag("username").unwrap_or_else(|_| "syui".to_string());
let user_handle = c.string_flag("handle").unwrap_or_else(|_| "syui.ai".to_string());
let user_did = c.string_flag("did").unwrap_or_else(|_| "did:plc:uqzpqmrjnptsxezjx4xuh2mn".to_string());
let chara = c.string_flag("character").unwrap_or_else(|_| "ai".to_string());
let limit = c.int_flag("limit")?.try_into()?;
let aiten = c.int_flag("aiten")?.try_into()?;
let lv = c.int_flag("lv")?.try_into()?;
let exp = c.int_flag("exp")?.try_into()?;
let hp = c.int_flag("hp")?.try_into()?;
let rank = c.int_flag("rank")?.try_into()?;
let mode = c.int_flag("mode")?.try_into()?;
let attach = c.int_flag("attach")?.try_into()?;
let critical = c.int_flag("critical")?.try_into()?;
let critical_d = c.int_flag("critical_d")?.try_into()?;
if data_toml(&"handle") == "yui.syui.ai" {
let str = post_game_user::post_request(col, user_name, user_did, user_handle, aiten, limit, chara, lv, exp, hp, rank, mode, attach, critical, critical_d);
println!("{}", str.await);
Ok(())
} else {
Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized")))
}
}
fn game_user(c: &Context) {
refresh(c);
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async {
if let Err(e) = c_game_user(c).await {
eprintln!("Error: {}", e);
}
});
}
async fn c_game_login(c: &Context) -> Result<(), Box<dyn std::error::Error>> {
let col = c.string_flag("col").unwrap_or_else(|_| "ai.syui.game.login".to_string());
let user_name = c.string_flag("username").unwrap_or_else(|_| "syui".to_string());
let account = "at://did:plc:4hqjfn7m6n5hno3doamuhgef/ai.syui.game.user/".to_string() + &user_name;
let login = c.bool_flag("login");
if data_toml(&"handle") == "yui.syui.ai" {
let str = post_game_login::post_request(col, user_name, login, account);
println!("{}", str.await);
Ok(())
} else {
Err(Box::new(std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Not authorized")))
}
}
fn game_login(c: &Context) {
refresh(c);
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async {
if let Err(e) = c_game_login(c).await {
eprintln!("Error: {}", e);
}
});
}
fn repost(c: &Context) { 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") {
@@ -397,6 +844,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");
@@ -420,10 +872,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);
@@ -432,10 +884,16 @@ 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;
let profile: ProfileIdentityResolve = serde_json::from_str(&str).unwrap(); let profile: ProfileIdentityResolve = serde_json::from_str(&str).unwrap();
let col = c.string_flag("col").unwrap_or_else(|_| "app.bsky.feed.post".to_string());
let udid = profile.did; let udid = profile.did;
let handle = m.to_string(); let handle = m.to_string();
let at = "@".to_owned() + &handle; let at = "@".to_owned() + &handle;
@@ -443,6 +901,7 @@ fn mention(c: &Context) {
let s = 0; let s = 0;
if let Ok(post) = c.string_flag("post") { if let Ok(post) = c.string_flag("post") {
let str = mention::post_request( let str = mention::post_request(
col,
post.to_string(), post.to_string(),
at.to_string(), at.to_string(),
udid.to_string(), udid.to_string(),
@@ -459,6 +918,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") {
@@ -498,6 +962,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";
@@ -529,6 +998,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();
@@ -561,11 +1035,13 @@ fn reply_og(c: &Context) {
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();
let uri = c.string_flag("uri").unwrap(); let uri = c.string_flag("uri").unwrap();
let cid_root = c.string_flag("cid-root").unwrap();
let uri_root = c.string_flag("uri-root").unwrap();
let title = c.string_flag("title").unwrap(); let title = c.string_flag("title").unwrap();
let desc = c.string_flag("description").unwrap(); let desc = c.string_flag("description").unwrap();
let img = c.string_flag("img").unwrap(); let img = c.string_flag("img").unwrap();
let h = async { let h = async {
let str = reply_og::post_request(m, link, cid, uri, img, title, desc); let str = reply_og::post_request(m, link, cid, uri, cid_root, uri_root, img, title, desc);
println!("{}", str.await); println!("{}", str.await);
}; };
let res = tokio::runtime::Runtime::new().unwrap().block_on(h); let res = tokio::runtime::Runtime::new().unwrap().block_on(h);
@@ -573,6 +1049,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;

View File

@@ -1,28 +1,24 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use serde_json::json; use serde_json::json;
pub async fn post_request(text: String, at: String, udid: String, s: i32, e: i32) -> String { pub async fn post_request(col: String, text: String, at: String, udid: String, s: i32, e: i32) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
let url = url(&"record_create"); let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"did": did.to_string(), "did": did.to_string(),
"repo": handle.to_string(), "repo": handle.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
"record": { "record": {
"text": at.to_string() + &" ".to_string() + &text.to_string(), "text": at.to_string() + &" ".to_string() + &text.to_string(),
"$type": "app.bsky.feed.post", "$type": col.to_string(),
"createdAt": d.to_string(), "createdAt": d.to_string(),
"facets": [ "facets": [
{ {
@@ -39,19 +35,12 @@ pub async fn post_request(text: String, at: String, udid: String, s: i32, e: i32
} }
] ]
}, },
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res; match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
} }

View File

@@ -1,30 +1,13 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_refresh;
use crate::url; use crate::url;
//use serde_json::json;
pub async fn get_request(limit: i32) -> String { pub async fn get_request(limit: i32) -> String {
let token = data_refresh(&"access"); let base_url = url(&"notify_list");
let url = url(&"notify_list"); let url = format!("{}?limit={}", base_url, limit);
let client = HttpClient::new();
let client = reqwest::Client::new(); match client.get_with_auth(&url).await {
let res = client Ok(response) => response,
.get(url) Err(_) => "err".to_string(),
.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;
}
} }
} }

View File

@@ -1,27 +1,17 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_refresh;
use crate::url; use crate::url;
use serde_json::json; use serde_json::json;
pub async fn post_request(time: String) -> String { pub async fn post_request(time: String) -> String {
let token = data_refresh(&"access");
let url = url(&"notify_update"); let url = url(&"notify_update");
let client = HttpClient::new();
let post = Some(json!({ let post = json!({
"seenAt": time.to_string(), "seenAt": time.to_string(),
})); });
let client = reqwest::Client::new(); match client.post_json_with_auth(&url, &post).await {
let res = client Ok(response) => response,
.post(url) Err(e) => format!("Error: {}", e),
.json(&post) }
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res;
} }

View File

@@ -33,7 +33,7 @@ pub async fn post_request(prompt: String) -> String {
"; ";
let post = Some(json!({ let post = Some(json!({
"model": "gpt-3.5-turbo", "model": "gpt-4o-mini",
"messages": [ "messages": [
{"role": "system", "content": &setting.to_string()}, {"role": "system", "content": &setting.to_string()},
{"role": "user", "content": &prompt.to_string()}, {"role": "user", "content": &prompt.to_string()},

View File

@@ -1,12 +1,10 @@
extern crate reqwest;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use crate::http_client::HttpClient;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use serde_json::json; use serde_json::json;
pub async fn post_request(text: String) -> String { pub async fn post_request(text: String) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
@@ -16,7 +14,7 @@ pub async fn post_request(text: String) -> String {
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -24,19 +22,14 @@ pub async fn post_request(text: String) -> String {
"text": text.to_string(), "text": text.to_string(),
"createdAt": d.to_string(), "createdAt": d.to_string(),
}, },
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client match client.post_json_with_auth(&url, &post).await {
.post(url) Ok(response) => response,
.json(&post) Err(e) => {
.header("Authorization", "Bearer ".to_owned() + &token) eprintln!("Error posting: {}", e);
.send() "err".to_string()
.await }
.unwrap() }
.text()
.await
.unwrap();
return res;
} }

View File

@@ -1,22 +1,19 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_toml; use crate::data_toml;
use crate::data_refresh;
use crate::url; use crate::url;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use serde_json::json; use serde_json::json;
pub async fn post_request(text: String, link: String, s: i32, e: i32) -> String { pub async fn post_request(text: String, link: String, s: i32, e: i32) -> String {
let token = data_refresh(&"access");
let did = data_toml(&"did"); let did = data_toml(&"did");
let handle = data_toml(&"handle"); let handle = data_toml(&"handle");
let url = url(&"record_create"); let url = url(&"record_create");
let col = "app.bsky.feed.post".to_string(); let col = "app.bsky.feed.post".to_string();
let d = Timestamp::now_utc(); let d = Timestamp::now_utc();
let d = d.to_string(); let d = d.to_string();
let post = Some(json!({ let post = json!({
"repo": handle.to_string(), "repo": handle.to_string(),
"did": did.to_string(), "did": did.to_string(),
"collection": col.to_string(), "collection": col.to_string(),
@@ -38,19 +35,12 @@ pub async fn post_request(text: String, link: String, s: i32, e: i32) -> String
} }
], ],
}, },
})); });
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client
.post(url)
.json(&post)
.header("Authorization", "Bearer ".to_owned() + &token)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res; match client.post_json_with_auth(&url, &post).await {
Ok(response) => response,
Err(e) => format!("Error: {}", e),
}
} }

View File

@@ -1,21 +1,15 @@
extern crate reqwest;
use crate::data_refresh;
use crate::url; use crate::url;
use crate::http_client::HttpClient;
pub async fn get_request(user: String) -> String { pub async fn get_request(user: String) -> String {
let token = data_refresh(&"access");
let url = url(&"profile_get") + &"?handle=" + &user; let url = url(&"profile_get") + &"?handle=" + &user;
let client = reqwest::Client::new(); let client = HttpClient::new();
let res = client match client.get_with_auth(&url).await {
.get(url) Ok(response) => response,
.header("Authorization", "Bearer ".to_owned() + &token) Err(e) => {
.send() eprintln!("Error getting profile: {}", e);
.await "err".to_string()
.unwrap() }
.text() }
.await
.unwrap();
return res;
} }

View File

@@ -1,21 +1,18 @@
extern crate reqwest; use crate::http_client::HttpClient;
use crate::data_toml; use crate::data_toml;
use crate::url; use crate::url;
pub async fn post_request() -> String { pub async fn post_request() -> String {
let refresh = data_toml(&"refresh"); let refresh = data_toml(&"refresh");
let url = url(&"session_refresh"); let url = url(&"session_refresh");
let client = HttpClient::new();
let client = reqwest::Client::new(); let auth_header = format!("Bearer {}", refresh);
let res = client let headers = vec![("Authorization", auth_header.as_str())];
.post(url) let empty_json = serde_json::json!({});
.header("Authorization", "Bearer ".to_owned() + &refresh)
.send()
.await
.unwrap()
.text()
.await
.unwrap();
return res; match client.post_with_headers(&url, &empty_json, headers).await {
Ok(response) => response,
Err(_) => "err".to_string(),
}
} }

Some files were not shown because too many files have changed in this diff Show More