Compare commits
	
		
			5 Commits
		
	
	
		
			d2a394cec2
			...
			claude-cod
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						a17d2c9d66
	
				 | 
					
					
						|||
| 
						
						
							
						
						998777d46a
	
				 | 
					
					
						|||
| 
						
						
							
						
						61d7df6922
	
				 | 
					
					
						|||
| 
						
						
							
						
						eb8f1b17c8
	
				 | 
					
					
						|||
| 
						
						
							
						
						fc5e942f0c
	
				 | 
					
					
						
							
								
								
									
										22
									
								
								.claude/settings.local.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								.claude/settings.local.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
{
 | 
			
		||||
  "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:*)"
 | 
			
		||||
    ],
 | 
			
		||||
    "deny": []
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 Submodule .config/ai/scpt updated: 9cbca76fc7...a1905d104b
									
								
							
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
Cargo.lock
 | 
			
		||||
target
 | 
			
		||||
*.json
 | 
			
		||||
#*.json
 | 
			
		||||
*.DS_Store
 | 
			
		||||
**.DS_Store
 | 
			
		||||
scpt/json/
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								Cargo.toml
									
									
									
									
									
								
							@@ -1,9 +1,17 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "ai"
 | 
			
		||||
name = "aibot"
 | 
			
		||||
authors = ["syui"]
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
description = "latest@2024-08-18"
 | 
			
		||||
description = "ai.bot - Bluesky AT Protocol Bot"
 | 
			
		||||
 | 
			
		||||
[[bin]]
 | 
			
		||||
name = "aibot"
 | 
			
		||||
path = "src/main.rs"
 | 
			
		||||
 | 
			
		||||
[[bin]]
 | 
			
		||||
name = "ai"
 | 
			
		||||
path = "src/alias.rs"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
seahorse = "*"
 | 
			
		||||
@@ -19,3 +27,7 @@ rustc-serialize = "*"
 | 
			
		||||
toml = "*"
 | 
			
		||||
iso8601-timestamp = "*"
 | 
			
		||||
sysinfo = "*"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
mockito = "1.2"
 | 
			
		||||
tokio-test = "0.4"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
FROM syui/aios
 | 
			
		||||
ADD .ssh /root/.ssh
 | 
			
		||||
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
ADD ./test/entrypoint.sh .
 | 
			
		||||
RUN chmod +x /root/entrypoint.sh
 | 
			
		||||
RUN pacman -Syu bc --noconfirm
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/root/entrypoint.sh"]
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,14 @@ command = "cargo"
 | 
			
		||||
args = ["test"]
 | 
			
		||||
dependencies = ["clean"]
 | 
			
		||||
 | 
			
		||||
[tasks.test-quick]
 | 
			
		||||
command = "cargo"
 | 
			
		||||
args = ["test"]
 | 
			
		||||
 | 
			
		||||
[tasks.test-verbose]
 | 
			
		||||
command = "cargo"
 | 
			
		||||
args = ["test", "--", "--nocapture"]
 | 
			
		||||
 | 
			
		||||
[tasks.my-flow]
 | 
			
		||||
dependencies = [
 | 
			
		||||
    "format",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								README.md
									
									
									
									
									
								
							@@ -107,3 +107,38 @@ ADMIN=syui.syu.is
 | 
			
		||||
$ docker compose build
 | 
			
		||||
$ docker compose up -d
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 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
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								docs/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								docs/README.md
									
									
									
									
									
										Normal 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)の「コントリビューション」章を参照してください。
 | 
			
		||||
							
								
								
									
										332
									
								
								docs/development-guide.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								docs/development-guide.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,332 @@
 | 
			
		||||
# ai.bot 開発ガイド
 | 
			
		||||
 | 
			
		||||
## プロジェクト概要
 | 
			
		||||
 | 
			
		||||
ai.botは、Rust製のBluesky(AT 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
									
								
							
							
						
						
									
										334
									
								
								docs/http-client-api.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										271
									
								
								docs/migration-guide.md
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										208
									
								
								docs/refactoring-summary.md
									
									
									
									
									
										Normal 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通信部分が大幅に改善され、今後の開発・保守が容易になりました。
 | 
			
		||||
							
								
								
									
										37
									
								
								scripts/setup-migration.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										37
									
								
								scripts/setup-migration.sh
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										39
									
								
								src/alias.rs
									
									
									
									
									
										Normal 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								src/bot.rs
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/bot.rs
									
									
									
									
									
								
							@@ -431,6 +431,42 @@ pub fn c_bot(c: &Context) {
 | 
			
		||||
                        println!("{}", str_rep);
 | 
			
		||||
                        w_cid(cid.to_string(), log_file(&"n1"), true);
 | 
			
		||||
                    }
 | 
			
		||||
                } else if com == "game" || com == "/game" {
 | 
			
		||||
                    let output = Command::new(data_scpt(&"ai"))
 | 
			
		||||
                        .arg(&"atproto").arg(&"game")
 | 
			
		||||
                        .arg(&handle)
 | 
			
		||||
                        .arg(&did)
 | 
			
		||||
                        .arg(&cid)
 | 
			
		||||
                        .arg(&uri)
 | 
			
		||||
                        .arg(&cid_root)
 | 
			
		||||
                        .arg(&uri_root)
 | 
			
		||||
                        .arg(&host)
 | 
			
		||||
                        .arg(&prompt)
 | 
			
		||||
                        .arg(&prompt_sub)
 | 
			
		||||
                        .output()
 | 
			
		||||
                        .expect("zsh");
 | 
			
		||||
                    let d = String::from_utf8_lossy(&output.stdout);
 | 
			
		||||
                    let dd = "\n".to_owned() + &d.to_string();
 | 
			
		||||
                    let text_limit = c_char(dd);
 | 
			
		||||
                    handlev = d.lines().collect::<Vec<_>>()[0].to_string();
 | 
			
		||||
                    link = "https://card.syui.ai/".to_owned() + &handlev;
 | 
			
		||||
                    println!("{}", e);
 | 
			
		||||
                    e = link.chars().count();
 | 
			
		||||
                    if text_limit.len() > 3 {
 | 
			
		||||
                        let str_rep = reply_link::post_request(
 | 
			
		||||
                            text_limit.to_string(),
 | 
			
		||||
                            link.to_string(),
 | 
			
		||||
                            s,
 | 
			
		||||
                            e.try_into().unwrap(),
 | 
			
		||||
                            cid.to_string(),
 | 
			
		||||
                            uri.to_string(),
 | 
			
		||||
                            cid_root.to_string(),
 | 
			
		||||
                            uri_root.to_string(),
 | 
			
		||||
                        )
 | 
			
		||||
                        .await;
 | 
			
		||||
                        println!("{}", str_rep);
 | 
			
		||||
                        w_cid(cid.to_string(), log_file(&"n1"), true);
 | 
			
		||||
                    }
 | 
			
		||||
                } else if com == "quiz" || com == "/quiz" {
 | 
			
		||||
                    println!("admin:{}", admin);
 | 
			
		||||
                    let output = Command::new(data_scpt(&"ai"))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										103
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								src/data.rs
									
									
									
									
									
								
							@@ -8,35 +8,91 @@ use std::io::Write;
 | 
			
		||||
use std::path::Path;
 | 
			
		||||
 | 
			
		||||
pub fn data_file(s: &str) -> String {
 | 
			
		||||
    let file = "/.config/ai/";
 | 
			
		||||
    let mut f = shellexpand::tilde("~").to_string();
 | 
			
		||||
    f.push_str(&file);
 | 
			
		||||
    let path = Path::new(&f);
 | 
			
		||||
    if path.is_dir() == false {
 | 
			
		||||
        let _ = fs::create_dir_all(f.clone());
 | 
			
		||||
    // 新しい設定ディレクトリ(優先)
 | 
			
		||||
    let new_config_dir = "/.config/syui/ai/bot/";
 | 
			
		||||
    let mut new_path = shellexpand::tilde("~").to_string();
 | 
			
		||||
    new_path.push_str(&new_config_dir);
 | 
			
		||||
    
 | 
			
		||||
    // 旧設定ディレクトリ(互換性のため)
 | 
			
		||||
    let old_config_dir = "/.config/ai/";
 | 
			
		||||
    let mut old_path = shellexpand::tilde("~").to_string();
 | 
			
		||||
    old_path.push_str(&old_config_dir);
 | 
			
		||||
    
 | 
			
		||||
    // 新しいディレクトリを作成
 | 
			
		||||
    let new_dir = Path::new(&new_path);
 | 
			
		||||
    if !new_dir.is_dir() {
 | 
			
		||||
        let _ = fs::create_dir_all(new_path.clone());
 | 
			
		||||
    }
 | 
			
		||||
    match &*s {
 | 
			
		||||
        "toml" => f + &"token.toml",
 | 
			
		||||
        "json" => f + &"token.json",
 | 
			
		||||
        "refresh" => f + &"refresh.toml",
 | 
			
		||||
        _ => f + &"." + &s,
 | 
			
		||||
    
 | 
			
		||||
    let filename = match &*s {
 | 
			
		||||
        "toml" => "token.toml",
 | 
			
		||||
        "json" => "token.json", 
 | 
			
		||||
        "refresh" => "refresh.toml",
 | 
			
		||||
        _ => &format!(".{}", s),
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    let new_file = new_path.clone() + filename;
 | 
			
		||||
    let old_file = old_path + filename;
 | 
			
		||||
    
 | 
			
		||||
    // 新しいパスにファイルが存在する場合は新しいパスを使用
 | 
			
		||||
    if Path::new(&new_file).exists() {
 | 
			
		||||
        return new_file;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 旧パスにファイルが存在し、新しいパスに存在しない場合は移行を試行
 | 
			
		||||
    if Path::new(&old_file).exists() && !Path::new(&new_file).exists() {
 | 
			
		||||
        if let Ok(_) = fs::copy(&old_file, &new_file) {
 | 
			
		||||
            eprintln!("Migrated config file: {} -> {}", old_file, new_file);
 | 
			
		||||
            return new_file;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // デフォルトは新しいパス
 | 
			
		||||
    new_file
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn log_file(s: &str) -> String {
 | 
			
		||||
    let file = "/.config/ai/txt/";
 | 
			
		||||
    let mut f = shellexpand::tilde("~").to_string();
 | 
			
		||||
    f.push_str(&file);
 | 
			
		||||
    let path = Path::new(&f);
 | 
			
		||||
    if path.is_dir() == false {
 | 
			
		||||
        let _ = fs::create_dir_all(f.clone());
 | 
			
		||||
    // 新しい設定ディレクトリ(優先)
 | 
			
		||||
    let new_log_dir = "/.config/syui/ai/bot/txt/";
 | 
			
		||||
    let mut new_path = shellexpand::tilde("~").to_string();
 | 
			
		||||
    new_path.push_str(&new_log_dir);
 | 
			
		||||
    
 | 
			
		||||
    // 旧設定ディレクトリ(互換性のため)
 | 
			
		||||
    let old_log_dir = "/.config/ai/txt/";
 | 
			
		||||
    let mut old_path = shellexpand::tilde("~").to_string();
 | 
			
		||||
    old_path.push_str(&old_log_dir);
 | 
			
		||||
    
 | 
			
		||||
    // 新しいディレクトリを作成
 | 
			
		||||
    let new_dir = Path::new(&new_path);
 | 
			
		||||
    if !new_dir.is_dir() {
 | 
			
		||||
        let _ = fs::create_dir_all(new_path.clone());
 | 
			
		||||
    }
 | 
			
		||||
    match &*s {
 | 
			
		||||
        "n1" => f + &"notify_cid.txt",
 | 
			
		||||
        "n2" => f + &"notify_cid_run.txt",
 | 
			
		||||
        "c1" => f + &"comment_cid.txt",
 | 
			
		||||
        _ => f + &s,
 | 
			
		||||
    
 | 
			
		||||
    let filename = match &*s {
 | 
			
		||||
        "n1" => "notify_cid.txt",
 | 
			
		||||
        "n2" => "notify_cid_run.txt", 
 | 
			
		||||
        "c1" => "comment_cid.txt",
 | 
			
		||||
        _ => s,
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    let new_file = new_path.clone() + filename;
 | 
			
		||||
    let old_file = old_path + filename;
 | 
			
		||||
    
 | 
			
		||||
    // 新しいパスにファイルが存在する場合は新しいパスを使用
 | 
			
		||||
    if Path::new(&new_file).exists() {
 | 
			
		||||
        return new_file;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // 旧パスにファイルが存在し、新しいパスに存在しない場合は移行を試行
 | 
			
		||||
    if Path::new(&old_file).exists() && !Path::new(&new_file).exists() {
 | 
			
		||||
        if let Ok(_) = fs::copy(&old_file, &new_file) {
 | 
			
		||||
            eprintln!("Migrated log file: {} -> {}", old_file, new_file);
 | 
			
		||||
            return new_file;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // デフォルトは新しいパス
 | 
			
		||||
    new_file
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Token {
 | 
			
		||||
@@ -107,6 +163,7 @@ pub struct BaseUrl {
 | 
			
		||||
    pub record_list: String,
 | 
			
		||||
    pub record_create: String,
 | 
			
		||||
    pub record_delete: String,
 | 
			
		||||
    pub record_put: String,
 | 
			
		||||
    pub session_create: String,
 | 
			
		||||
    pub session_refresh: String,
 | 
			
		||||
    pub session_get: String,
 | 
			
		||||
@@ -142,6 +199,7 @@ pub fn url(s: &str) -> String {
 | 
			
		||||
    let baseurl = BaseUrl {
 | 
			
		||||
        profile_get: "com.atproto.identity.resolveHandle".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_delete: "com.atproto.repo.deleteRecord".to_string(),
 | 
			
		||||
        describe: "com.atproto.repo.describeRepo".to_string(),
 | 
			
		||||
@@ -173,6 +231,7 @@ pub fn url(s: &str) -> String {
 | 
			
		||||
        "record_list" => t.to_string() + &baseurl.record_list,
 | 
			
		||||
        "record_create" => t.to_string() + &baseurl.record_create,
 | 
			
		||||
        "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_refresh" => t.to_string() + &baseurl.session_refresh,
 | 
			
		||||
        "session_get" => t.to_string() + &baseurl.session_get,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,21 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(rkey: String, col: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    //let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_delete");
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "rkey": rkey.to_string(),
 | 
			
		||||
        "collection": col.to_string()
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,13 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
//use crate::data_toml;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(user: String) -> String {
 | 
			
		||||
    //let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"describe");
 | 
			
		||||
    let base_url = url(&"describe");
 | 
			
		||||
    let url = format!("{}?repo={}", base_url, user);
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .query(&[("repo", &user)])
 | 
			
		||||
        //.header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.get(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,13 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(feed: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"feed_get");
 | 
			
		||||
    let feed = feed.to_string();
 | 
			
		||||
    //let col = "app.bsky.feed.generator".to_string();
 | 
			
		||||
    let base_url = url(&"feed_get");
 | 
			
		||||
    let url = format!("{}?feed={}", base_url, feed);
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .query(&[("feed", feed)])
 | 
			
		||||
        //.query(&[("feed", feed), ("collection", col)])
 | 
			
		||||
        .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();
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(_) => "err".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
        Err(_e) => {
 | 
			
		||||
            let e = "err".to_string();
 | 
			
		||||
            return e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,12 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
//use crate::data::Follow;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(u: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
@@ -18,7 +16,7 @@ pub async fn post_request(u: String) -> String {
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -26,25 +24,19 @@ pub async fn post_request(u: String) -> String {
 | 
			
		||||
            "subject": u.to_string(),
 | 
			
		||||
            "createdAt": d.to_string(),
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error following user: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn delete_request(u: String, rkey: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    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 = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -63,19 +55,14 @@ pub async fn delete_request(u: String, rkey: String) -> String {
 | 
			
		||||
            "subject": u.to_string(),
 | 
			
		||||
            "createdAt": d.to_string(),
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error unfollowing user: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,14 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
//use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(actor: String, cursor: Option<String>) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"followers");
 | 
			
		||||
    let base_url = url(&"followers");
 | 
			
		||||
    let cursor = cursor.unwrap();
 | 
			
		||||
    let url = format!("{}?actor={}&cursor={}", base_url, actor, cursor);
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .query(&[("actor", actor), ("cursor", cursor)])
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,14 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
//use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(actor: String, cursor: Option<String>) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"follows");
 | 
			
		||||
    let base_url = url(&"follows");
 | 
			
		||||
    let cursor = cursor.unwrap();
 | 
			
		||||
    //let cursor = "1682386039125::bafyreihwgwozmvqxcxrhbr65agcaa4v357p27ccrhzkjf3mz5xiozjvzfa".to_string();
 | 
			
		||||
    //let cursor = "1682385956974::bafyreihivhux5m3sxbg33yruhw5ozhahwspnuqdsysbo57smzgptdcluem".to_string();
 | 
			
		||||
    let url = format!("{}?actor={}&cursor={}", base_url, actor, cursor);
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .query(&[("actor", actor), ("cursor", cursor)])
 | 
			
		||||
        //cursor.unwrap()
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								src/game.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/game.rs
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										35
									
								
								src/game/post_card.rs
									
									
									
									
									
										Normal 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),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								src/game/post_card_verify.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/game/post_card_verify.rs
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										30
									
								
								src/game/post_game.rs
									
									
									
									
									
										Normal 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),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								src/game/post_game_login.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/game/post_game_login.rs
									
									
									
									
									
										Normal 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),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/game/post_game_user.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/game/post_game_user.rs
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										114
									
								
								src/http_client.rs
									
									
									
									
									
										Normal 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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/img.rs
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/img.rs
									
									
									
									
									
								
							@@ -1,23 +1,19 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(text: String, link: String) -> String {
 | 
			
		||||
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.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 res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
@@ -12,17 +11,15 @@ pub async fn post_request(
 | 
			
		||||
    uri: String,
 | 
			
		||||
    itype: String,
 | 
			
		||||
) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -56,19 +53,12 @@ pub async fn post_request(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,19 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(text: String, link: String) -> String {
 | 
			
		||||
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.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 res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/like.rs
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/like.rs
									
									
									
									
									
								
							@@ -1,12 +1,10 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(cid: String, uri: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    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 = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -27,19 +25,14 @@ pub async fn post_request(cid: String, uri: String) -> String {
 | 
			
		||||
            },
 | 
			
		||||
            "createdAt": d.to_string(),
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error liking post: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										309
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										309
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -12,6 +12,11 @@ use crate::data::url;
 | 
			
		||||
use crate::data::w_cfg;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
@@ -22,6 +27,7 @@ pub mod describe;
 | 
			
		||||
pub mod follow;
 | 
			
		||||
pub mod followers;
 | 
			
		||||
pub mod follows;
 | 
			
		||||
pub mod http_client;
 | 
			
		||||
pub mod img_reply;
 | 
			
		||||
pub mod like;
 | 
			
		||||
pub mod mention;
 | 
			
		||||
@@ -30,6 +36,7 @@ pub mod notify_read;
 | 
			
		||||
pub mod openai;
 | 
			
		||||
pub mod post;
 | 
			
		||||
pub mod post_link;
 | 
			
		||||
pub mod game;
 | 
			
		||||
pub mod profile;
 | 
			
		||||
pub mod refresh;
 | 
			
		||||
pub mod reply;
 | 
			
		||||
@@ -43,6 +50,9 @@ pub mod feed_get;
 | 
			
		||||
pub mod feed_watch;
 | 
			
		||||
pub mod delete_record;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let args: Vec<String> = env::args().collect();
 | 
			
		||||
    let app = App::new(env!("CARGO_PKG_NAME"))
 | 
			
		||||
@@ -154,6 +164,160 @@ fn main() {
 | 
			
		||||
                .alias("c"),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        .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::new("like")
 | 
			
		||||
            .description("like <cid> -u <uri>")
 | 
			
		||||
@@ -234,6 +398,10 @@ fn main() {
 | 
			
		||||
                Flag::new("post", FlagType::String)
 | 
			
		||||
                .alias("p"),
 | 
			
		||||
            )
 | 
			
		||||
            .flag(
 | 
			
		||||
                Flag::new("col", FlagType::String)
 | 
			
		||||
                .alias("c"),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        .command(
 | 
			
		||||
            Command::new("follow")
 | 
			
		||||
@@ -474,6 +642,145 @@ fn like(c: &Context) {
 | 
			
		||||
    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) {
 | 
			
		||||
    refresh(c);
 | 
			
		||||
    let m = c.args[0].to_string();
 | 
			
		||||
@@ -528,6 +835,7 @@ fn mention(c: &Context) {
 | 
			
		||||
    let h = async {
 | 
			
		||||
        let str = profile::get_request(m.to_string()).await;
 | 
			
		||||
        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 handle = m.to_string();
 | 
			
		||||
        let at = "@".to_owned() + &handle;
 | 
			
		||||
@@ -535,6 +843,7 @@ fn mention(c: &Context) {
 | 
			
		||||
        let s = 0;
 | 
			
		||||
        if let Ok(post) = c.string_flag("post") {
 | 
			
		||||
            let str = mention::post_request(
 | 
			
		||||
                col,
 | 
			
		||||
                post.to_string(),
 | 
			
		||||
                at.to_string(),
 | 
			
		||||
                udid.to_string(),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,24 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(text: String, at: String, udid: String, s: i32, e: i32) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
pub async fn post_request(col: String, text: String, at: String, udid: String, s: i32, e: i32) -> String {
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
        "record": {
 | 
			
		||||
            "text": at.to_string() + &" ".to_string() + &text.to_string(),
 | 
			
		||||
            "$type": "app.bsky.feed.post",
 | 
			
		||||
            "$type": col.to_string(),
 | 
			
		||||
            "createdAt": d.to_string(),
 | 
			
		||||
            "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 res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,30 +1,13 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
//use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(limit: i32) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"notify_list");
 | 
			
		||||
    let base_url = url(&"notify_list");
 | 
			
		||||
    let url = format!("{}?limit={}", base_url, limit);
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .query(&[("limit", limit)])
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let status_ref = res.error_for_status_ref();
 | 
			
		||||
 | 
			
		||||
    match status_ref {
 | 
			
		||||
        Ok(_) => {
 | 
			
		||||
            return res.text().await.unwrap();
 | 
			
		||||
        }
 | 
			
		||||
        Err(_e) => {
 | 
			
		||||
            let e = "err".to_string();
 | 
			
		||||
            return e;
 | 
			
		||||
        }
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(_) => "err".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,17 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(time: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"notify_update");
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "seenAt": time.to_string(),
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/post.rs
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/post.rs
									
									
									
									
									
								
							@@ -1,12 +1,10 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(text: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
@@ -16,7 +14,7 @@ pub async fn post_request(text: String) -> String {
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -24,19 +22,14 @@ pub async fn post_request(text: String) -> String {
 | 
			
		||||
            "text": text.to_string(),
 | 
			
		||||
            "createdAt": d.to_string(),
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error posting: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,19 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
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 handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.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 res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,15 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(user: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"profile_get") + &"?handle=" + &user;
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error getting profile: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,18 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::url;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request() -> String {
 | 
			
		||||
    let refresh = data_toml(&"refresh");
 | 
			
		||||
    let url = url(&"session_refresh");
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &refresh)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let auth_header = format!("Bearer {}", refresh);
 | 
			
		||||
    let headers = vec![("Authorization", auth_header.as_str())];
 | 
			
		||||
    let empty_json = serde_json::json!({});
 | 
			
		||||
    
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    match client.post_with_headers(&url, &empty_json, headers).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(_) => "err".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/reply.rs
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/reply.rs
									
									
									
									
									
								
							@@ -1,7 +1,6 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
@@ -12,7 +11,6 @@ pub async fn post_request(
 | 
			
		||||
    cid_root: String,
 | 
			
		||||
    uri_root: String,
 | 
			
		||||
) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
@@ -23,7 +21,7 @@ pub async fn post_request(
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -41,19 +39,14 @@ pub async fn post_request(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error replying to post: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
@@ -15,17 +14,15 @@ pub async fn post_request(
 | 
			
		||||
    cid_root: String,
 | 
			
		||||
    uri_root: String,
 | 
			
		||||
) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -57,19 +54,12 @@ pub async fn post_request(
 | 
			
		||||
            }
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
@@ -16,17 +15,15 @@ pub async fn post_request(
 | 
			
		||||
    title: String,
 | 
			
		||||
    description: String,
 | 
			
		||||
) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    let handle = data_toml(&"handle");
 | 
			
		||||
 | 
			
		||||
    let url = url(&"record_create");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
 | 
			
		||||
    let d = Timestamp::now_utc();
 | 
			
		||||
    let d = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -60,19 +57,12 @@ pub async fn post_request(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,10 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_toml;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::url;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use iso8601_timestamp::Timestamp;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(cid: String, uri: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let did = data_toml(&"did");
 | 
			
		||||
    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 = d.to_string();
 | 
			
		||||
 | 
			
		||||
    let post = Some(json!({
 | 
			
		||||
    let post = json!({
 | 
			
		||||
        "repo": handle.to_string(),
 | 
			
		||||
        "did": did.to_string(),
 | 
			
		||||
        "collection": col.to_string(),
 | 
			
		||||
@@ -27,19 +25,14 @@ pub async fn post_request(cid: String, uri: String) -> String {
 | 
			
		||||
            },
 | 
			
		||||
            "createdAt": d.to_string(),
 | 
			
		||||
        },
 | 
			
		||||
    }));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&post)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    match client.post_json_with_auth(&url, &post).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            eprintln!("Error reposting: {}", e);
 | 
			
		||||
            "err".to_string()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,12 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request() -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"session_get");
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let status_ref = res.error_for_status_ref();
 | 
			
		||||
 | 
			
		||||
    match status_ref {
 | 
			
		||||
        Ok(_) => {
 | 
			
		||||
            return res.text().await.unwrap();
 | 
			
		||||
        }
 | 
			
		||||
        Err(_e) => {
 | 
			
		||||
            let e = "err".to_string();
 | 
			
		||||
            return e;
 | 
			
		||||
        }
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(_) => "err".to_string(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								src/tests/http_client_tests.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/tests/http_client_tests.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_http_client_creation() {
 | 
			
		||||
    let _client = HttpClient::new();
 | 
			
		||||
    // HttpClientが正しく作成されることを確認
 | 
			
		||||
    let _client2 = HttpClient::default();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::test]
 | 
			
		||||
async fn test_http_client_error_handling() {
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    // 無効なURLでエラーが返ることを確認
 | 
			
		||||
    let result = client.get("http://invalid-url-that-does-not-exist.local").await;
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// モジュールが正しくコンパイルされることを確認
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_module_imports() {
 | 
			
		||||
    // モジュールが存在することを確認
 | 
			
		||||
    assert!(true);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								src/tests/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/tests/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod http_client_tests;
 | 
			
		||||
@@ -1,27 +1,14 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::data_refresh;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use crate::url;
 | 
			
		||||
 | 
			
		||||
pub async fn get_request(actor: String) -> String {
 | 
			
		||||
    let token = data_refresh(&"access");
 | 
			
		||||
    let url = url(&"record_list");
 | 
			
		||||
 | 
			
		||||
    let actor = actor.to_string();
 | 
			
		||||
    //let cursor = cursor.unwrap();
 | 
			
		||||
 | 
			
		||||
    let base_url = url(&"record_list");
 | 
			
		||||
    let col = "app.bsky.feed.post".to_string();
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .get(url)
 | 
			
		||||
        .query(&[("repo", actor), ("collection", col)])
 | 
			
		||||
        //.query(&[("actor", actor),("cursor", cursor)])
 | 
			
		||||
        .header("Authorization", "Bearer ".to_owned() + &token)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let url = format!("{}?repo={}&collection={}", base_url, actor, col);
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.get_with_auth(&url).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								src/token.rs
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/token.rs
									
									
									
									
									
								
							@@ -1,25 +1,17 @@
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use crate::http_client::HttpClient;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
pub async fn post_request(handle: String, pass: String, host: String) -> String {
 | 
			
		||||
    let url = "https://".to_owned()
 | 
			
		||||
        + &host.to_string()
 | 
			
		||||
        + &"/xrpc/com.atproto.server.createSession".to_string();
 | 
			
		||||
    let url = format!("https://{}/xrpc/com.atproto.server.createSession", host);
 | 
			
		||||
 | 
			
		||||
    let mut map = HashMap::new();
 | 
			
		||||
    map.insert("identifier", &handle);
 | 
			
		||||
    map.insert("password", &pass);
 | 
			
		||||
 | 
			
		||||
    let client = reqwest::Client::new();
 | 
			
		||||
    let res = client
 | 
			
		||||
        .post(url)
 | 
			
		||||
        .json(&map)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .text()
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let client = HttpClient::new();
 | 
			
		||||
    
 | 
			
		||||
    return res;
 | 
			
		||||
    match client.post_json(&url, &map).await {
 | 
			
		||||
        Ok(response) => response,
 | 
			
		||||
        Err(e) => format!("Error: {}", e),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
#!/bin/zsh
 | 
			
		||||
 | 
			
		||||
ai l $HANDLE -p $PASSWORD -s $HOST && ai bot -a $ADMIN
 | 
			
		||||
#ai l $HANDLE -p $PASSWORD -s $HOST
 | 
			
		||||
ai bot -a $ADMIN
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user