chaser-util 0.1.1

CHaser Online MeetingPlace / game-server scraper with C FFI, proxy support, and real-time map polling
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# chaser-util 使い方ガイド


## 目次


1. [インストール]#インストール
2. [モジュール概要]#モジュール概要
3. [room_list — ルーム一覧・ログインユーザー取得]#room_list--ルーム一覧ログインユーザー取得
4. [realtime_map_view — リアルタイムマップ取得(単発)]#realtime_map_view--リアルタイムマップ取得単発
5. [poll_realtime_map_view — リアルタイムマップ取得(ポーリング)]#poll_realtime_map_view--リアルタイムマップ取得ポーリング
6. [vs_result — 対戦結果取得]#vs_result--対戦結果取得
7. [プロキシ設定]#プロキシ設定
8. [フィルタリング詳細]#フィルタリング詳細
9. [C / C++ FFI]#c--c-ffi
10. [エラーハンドリング]#エラーハンドリング

---

## インストール


`Cargo.toml` に追記します。

```toml
[dependencies]
chaser-util = "0.1"
tokio = { version = "1", features = ["full"] }
```

---

## モジュール概要


| モジュール | 用途 |
|---|---|
| `room_list` | ミーティングプレイスのルーム一覧・ログインユーザー取得 |
| `realtime_map_view` | ゲームサーバーのマップ・プレイヤー情報を1回取得 |
| `poll_realtime_map_view` | マップ情報を一定間隔でポーリングし続ける |
| `vs_result` | 対戦結果一覧の取得 |

---

## room_list — ルーム一覧・ログインユーザー取得


### 基本的な使い方


```rust
use chaser_util::room_list::{scrape, ScrapeOptions};

#[tokio::main]

async fn main() {
    let result = scrape("ユーザー名", "パスワード", ScrapeOptions::default())
        .await
        .unwrap();

    // ルーム一覧
    for room in &result.rooms {
        println!(
            "ルーム{} 最大{}人 マップ表示:{} 公開日:{} 巡回:{} 備考:{}",
            room.room,
            room.max_connections,
            room.map_display,
            room.public_date,
            room.patrol,
            room.remarks,
        );
    }

    // ログイン中ユーザー(誰もいない場合は None)
    if let Some(users) = &result.logged_in_users {
        for user in users {
            println!(
                "{}番 {} ルーム{} 状態:{}",
                user.order, user.username, user.room, user.state
            );
        }
    } else {
        println!("ログイン中のユーザーはいません");
    }
}
```

### 手動プロキシを使う場合


```rust
use chaser_util::room_list::{scrape_with_proxy, ScrapeOptions};

let result = scrape_with_proxy(
    "ユーザー名",
    "パスワード",
    "http://192.168.1.1:8080",  // "" を渡すと直接接続
    ScrapeOptions::default(),
).await.unwrap();
```

### フィルタリング


```rust
use chaser_util::room_list::{scrape, RoomFilter, UserFilter, ScrapeOptions, MapDisplay, Patrol};

let room_filter = RoomFilter {
    room_min: Some(1),
    room_max: Some(10),              // ルーム番号 1〜10 のみ
    min_max_conn: Some(4),           // 最大接続数が4以上
    map_display: Some(MapDisplay::ENABLED.to_string()),  // マップ表示あり
    patrol: Some(Patrol::YES.to_string()),               // 巡回あり
    ..Default::default()
};

let user_filter = UserFilter {
    username_contains: Some("cool".to_string()),  // 名前に "cool" を含む
    ..Default::default()
};

let opts = ScrapeOptions::default()
    .with_room_filter(room_filter)
    .with_user_filter(user_filter);

let result = scrape("ユーザー名", "パスワード", opts).await.unwrap();
```

### RoomInfo フィールド一覧


| フィールド || 内容 |
|---|---|---|
| `room` | `u32` | ルーム番号 |
| `max_connections` | `u32` | 最大接続数 |
| `map_display` | `String` | マップ表示(`MapDisplay::ENABLED` / `DISABLED`|
| `public_date` | `String` | 公開日 |
| `patrol` | `String` | 巡回(`Patrol::YES` / `NO`|
| `remarks` | `String` | 備考(`Remarks::RA` / `SAI` / `ZEN` など) |

### LoggedInUser フィールド一覧


| フィールド || 内容 |
|---|---|---|
| `order` | `u32` | 接続順番号 |
| `username` | `String` | ユーザー名 |
| `room` | `u32` | 接続中のルーム番号 |
| `state` | `u32` | 状態 |

---

## realtime_map_view — リアルタイムマップ取得(単発)


ゲームサーバーのマップビューページから現在のマップ・ターン・プレイヤー情報を1回だけ取得します。

### 基本的な使い方


```rust
use chaser_util::realtime_map_view::{fetch_map_view, MapViewOptions};

#[tokio::main]

async fn main() {
    let result = fetch_map_view(
        "ユーザー名",
        "パスワード",
        MapViewOptions::default(),
    ).await.unwrap();

    println!("ルーム名: {}", result.room_name);
    println!("ターン: {}", result.turn);
    println!("次のプレイヤー: {}", result.next_player);
    println!("マップサイズ: {}行 x {}列",
        result.map.len(),
        result.map.first().map(|r| r.len()).unwrap_or(0)
    );

    // マップ表示
    for row in &result.map {
        for &tile in row {
            print!("{:03} ", tile);
        }
        println!();
    }

    // プレイヤー情報
    for player in &result.players {
        println!(
            "{}: A={} I={} P={} PD={} T={}",
            player.username,
            player.attr_a, player.attr_i,
            player.attr_p, player.attr_pd, player.attr_t
        );
        for cmd in &player.commands {
            println!("  コマンド: {}", cmd);
        }
    }
}
```

### タイル画像URLの取得


```rust
use chaser_util::realtime_map_view::tile_image_url;

// tile_id=12 → "http://.../img/012.gif"
let url = tile_image_url(12);
```

### MapViewResult フィールド一覧


| フィールド || 内容 |
|---|---|---|
| `room_name` | `String` | ルーム名(H1タグの `[...]` 内) |
| `turn` | `u32` | 現在のターン番号 |
| `next_player` | `String` | 次に行動するプレイヤー名 |
| `map` | `Vec<Vec<TileId>>` | マップの2次元配列(`[行][列]`|
| `players` | `Vec<PlayerInfo>` | プレイヤー情報一覧 |

### PlayerInfo フィールド一覧


| フィールド || 内容 |
|---|---|---|
| `username` | `String` | ユーザー名 |
| `attr_a` | `i32` | 攻撃力 (A) |
| `attr_i` | `i32` | 知性 (I) |
| `attr_p` | `i32` | パワー (P) |
| `attr_pd` | `i32` | パワー防御 (PD) |
| `attr_t` | `i32` | トータル (T) |
| `commands` | `Vec<String>` | コマンド一覧(例: `"gr 12,0,12"`|

---

## poll_realtime_map_view — リアルタイムマップ取得(ポーリング)


一度認証してJSESSIONIDを再利用しながら、指定間隔でマップを取得し続けます。セッション切れ時は自動で再認証します。

### 基本的な使い方


```rust
use std::time::Duration;
use chaser_util::poll_realtime_map_view::{poll_map_view, PollOptions};

#[tokio::main]

async fn main() {
    let mut rx = poll_map_view(
        "ユーザー名",
        "パスワード",
        Duration::from_secs(2),      // サーバーの更新間隔に合わせて2秒推奨
        PollOptions::default(),
    );

    while let Some(mv) = rx.recv().await {
        println!(
            "ターン={:4} 次={:10} マップ={}x{} プレイヤー={}",
            mv.turn,
            mv.next_player,
            mv.map.len(),
            mv.map.first().map(|r| r.len()).unwrap_or(0),
            mv.players.len(),
        );
    }
    // rx がドロップされると自動的にバックグラウンドタスクが終了する
}
```

### 一定ターン数だけ取得する例


```rust
let mut rx = poll_map_view("user", "pass", Duration::from_secs(2), PollOptions::default());
let mut count = 0;

while let Some(mv) = rx.recv().await {
    println!("ターン {}", mv.turn);
    count += 1;
    if count >= 20 {
        break;  // rx がドロップされ、バックグラウンドタスクも停止する
    }
}
```

### プロキシを指定する場合


```rust
use chaser_util::poll_realtime_map_view::PollOptions;

let opts = PollOptions {
    proxy_uri: Some("http://192.168.1.1:8080".to_string()),
};
// 直接接続の場合: proxy_uri: Some("".to_string())
// 自動検出の場合: proxy_uri: None  (デフォルト)
```

> **注意**: エラー(認証失敗・ネットワークエラー)は `eprintln!` で標準エラー出力に記録されます。成功したフレームのみチャンネルに送信されます。

---

## vs_result — 対戦結果取得


### 今日の対戦結果を全件取得


```rust
use chaser_util::vs_result::{fetch_vs_result, VsResultQuery};

#[tokio::main]

async fn main() {
    let results = fetch_vs_result(
        "ユーザー名",
        "パスワード",
        VsResultQuery::today(),   // 今日の日付を自動設定
        None,                     // プロキシ: None=自動検出
    ).await.unwrap();

    for battle in &results {
        println!(
            "ルーム{} {} 〜 {}",
            battle.room, battle.start_time, battle.end_time
        );
        for player in &battle.players {
            println!(
                "  {}番 {} | ターン取得:{} 残:{} 合計:{}pt",
                player.order, player.username,
                player.get_turn, player.rem_turn, player.total_point
            );
            // 詳細ポイント(存在しない場合もある)
            if let Some(ap) = player.action_point {
                println!("    行動:{} アイテム:{} 設置:{} ダメージ:{}",
                    ap,
                    player.item_point.unwrap_or(0),
                    player.put_point.unwrap_or(0),
                    player.put_damage.unwrap_or(0),
                );
            }
        }
    }
}
```

### 特定の日付を指定する


```rust
let results = fetch_vs_result(
    "user", "pass",
    VsResultQuery::for_date("2026-04-07"),
    None,
).await.unwrap();
```

### 検索条件を細かく指定する


```rust
let query = VsResultQuery {
    min_room: 1,
    max_room: 5,                          // ルーム1〜5のみ
    min_total_point: 0,                   // 合計ポイントが0以上
    min_start_date: "2026-04-01".to_string(),
    max_start_date: "2026-04-07".to_string(),  // 日付範囲指定
    // 他のフィールドはデフォルト値を使う
    ..VsResultQuery::today()
};
```

### BattleResult フィールド一覧


| フィールド || 内容 |
|---|---|---|
| `room` | `u32` | ルーム番号 |
| `start_time` | `String` | 対戦開始時刻 |
| `end_time` | `String` | 対戦終了時刻 |
| `players` | `Vec<PlayerResult>` | 参加プレイヤー一覧(接続順) |

### PlayerResult フィールド一覧


| フィールド || 内容 |
|---|---|---|
| `order` | `u32` | 接続順(1=先手) |
| `username` | `String` | ユーザー名 |
| `get_turn` | `i32` | 取得ターン数 |
| `rem_turn` | `i32` | 残りターン数 |
| `total_point` | `i32` | 合計ポイント |
| `action_point` | `Option<i32>` | 行動ポイント(ない場合あり) |
| `item_point` | `Option<i32>` | アイテムポイント(ない場合あり) |
| `put_point` | `Option<i32>` | 設置ポイント(ない場合あり) |
| `put_damage` | `Option<i32>` | 設置ダメージ(ない場合あり) |

---

## プロキシ設定


全モジュール共通で以下の3モードをサポートします。

| 設定値 | 動作 |
|---|---|
| `None` | 自動検出(環境変数 → Windowsレジストリ → macOS SCF → 直接接続) |
| `Some("")` | 強制直接接続 |
| `Some("http://host:port")` | 指定プロキシを使用 |

環境変数 `HTTP_PROXY` / `HTTPS_PROXY` も自動検出対象です。

---

## フィルタリング詳細


### RoomFilter フィールド一覧


| フィールド || 条件 |
|---|---|---|
| `room` | `Option<u32>` | ルーム番号が一致 |
| `room_min` | `Option<u32>` | ルーム番号がこれ以上 |
| `room_max` | `Option<u32>` | ルーム番号がこれ以下 |
| `min_max_conn` | `Option<u32>` | 最大接続数がこれ以上 |
| `max_max_conn` | `Option<u32>` | 最大接続数がこれ以下 |
| `map_display` | `Option<String>` | マップ表示が一致 |
| `public_date` | `Option<String>` | 公開日が完全一致 |
| `public_date_contains` | `Option<String>` | 公開日に文字列を含む |
| `patrol` | `Option<String>` | 巡回が一致 |
| `remarks` | `Option<String>` | 備考が完全一致 |
| `remarks_contains` | `Option<String>` | 備考に文字列を含む |

### UserFilter フィールド一覧


| フィールド || 条件 |
|---|---|---|
| `order` | `Option<u32>` | 接続番号が一致 |
| `order_min` | `Option<u32>` | 接続番号がこれ以上 |
| `order_max` | `Option<u32>` | 接続番号がこれ以下 |
| `username` | `Option<String>` | ユーザー名が完全一致 |
| `username_contains` | `Option<String>` | ユーザー名に文字列を含む |
| `room` | `Option<u32>` | ルーム番号が一致 |
| `room_min` | `Option<u32>` | ルーム番号がこれ以上 |
| `room_max` | `Option<u32>` | ルーム番号がこれ以下 |
| `state` | `Option<u32>` | 状態が一致 |

### 定数


```rust
use chaser_util::room_list::{MapDisplay, Patrol, Remarks};

MapDisplay::ENABLED   // "可"
MapDisplay::DISABLED  // "否"

Patrol::YES           // "有"
Patrol::NO            // "×"

Remarks::RA           // "ラ"
Remarks::SAI          // "埼"
Remarks::ZEN          // "全"
```

---

## C / C++ FFI


ライブラリを共有ライブラリとしてビルドし、`chaser-util.h` を使ってC/C++から呼び出せます。

### ビルド


```sh
cargo build --release
# Windows: target/release/chaser_util.dll  +  chaser_util.dll.lib

# Linux:   target/release/libchaser_util.so

```

### C++ での使い方


```cpp
#include "chaser-util.h"

#include <iostream>


int main() {
    // フィルターなしで取得
    auto result = chaser_util::scrape("ユーザー名", "パスワード");

    for (auto& room : result.rooms) {
        std::cout << "ルーム" << room.room
                  << " 最大" << room.max_connections << "人\n";
    }

    if (result.logged_in_users) {
        for (auto& user : *result.logged_in_users) {
            std::cout << user.username << " ルーム" << user.room << "\n";
        }
    }
}
```

### フィルターを使う場合(C++)


```cpp
chaser_util::RoomFilter rf;
rf.room_range(1, 10)                         // ルーム1〜10
  .min_max_conn(4)                            // 最大接続数4以上
  .map_display(chaser_util::MapDisplay::ENABLED);  // マップ表示あり

chaser_util::UserFilter uf;
uf.username_contains(u8"cool");

auto result = chaser_util::scrape("user", "pass", &rf, &uf);
```

### 手動プロキシ(C++)


```cpp
auto result = chaser_util::scrape_with_proxy(
    "user", "pass",
    "http://192.168.1.1:8080",  // "" で直接接続
    nullptr, nullptr
);
```

### エラー処理(C++)


C++ ラッパーはエラー時に `std::runtime_error` を投げます。

```cpp
try {
    auto result = chaser_util::scrape("user", "pass");
} catch (const std::runtime_error& e) {
    std::cerr << "エラー: " << e.what() << "\n";
}
```

### エラーコード一覧(C API)


| `error_code` | 意味 |
|---|---|
| `0` | 成功 |
| `1` | スクレイプ / ネットワークエラー(`scraper_last_error()` で詳細取得) |
| `2` | 不正な引数(`user` / `pass` / `proxy_uri` が NULL) |
| `3` | 内部パニック(発生した場合はバグ報告をお願いします) |

---

## エラーハンドリング


全ての非同期関数は `Result<T, Box<dyn std::error::Error + Send + Sync>>` を返します。

```rust
match scrape("user", "pass", ScrapeOptions::default()).await {
    Ok(result) => {
        println!("ルーム数: {}", result.rooms.len());
    }
    Err(e) => {
        eprintln!("取得失敗: {}", e);
        // ネットワークエラー、認証失敗、HTML解析エラーなどが含まれる
    }
}
```

`poll_map_view` はチャンネルを返す同期関数で、エラーはバックグラウンドタスク内で `eprintln!` に出力されます。チャンネルには成功したフレームのみ届きます。