feat: GmailWrapper(GoogleOAuth)を実装 (Google API フル活用)
This commit is contained in:
parent
665f2f6880
commit
1a89559648
3 changed files with 207 additions and 1 deletions
82
lib/services/gmail_wrapper.dart
Normal file
82
lib/services/gmail_wrapper.dart
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Version: 1.0.0
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:googleapis/gmail/v1.dart';
|
||||||
|
import 'package:googleapis_auth/google_auth.dart';
|
||||||
|
|
||||||
|
/// Gmail API を介した同期用メールリレー
|
||||||
|
///
|
||||||
|
/// P2P 通信不要なノード識別システムを提供します。
|
||||||
|
class GmailWrapper {
|
||||||
|
final String _gmailAddress; // BCC 用gmail アドレス(ノードキー)
|
||||||
|
final GAuthClient? _authClient; // OAuth 認証クライアント
|
||||||
|
final GmailService? _gmail;
|
||||||
|
|
||||||
|
/// 新しいインスタンスを作成
|
||||||
|
factory GmailWrapper({
|
||||||
|
required String gmailAddress,
|
||||||
|
bool useOAuth = true,
|
||||||
|
}) {
|
||||||
|
if (useOAuth) {
|
||||||
|
// 正式な OAuth2 フロー(credentials.json など)を使う場合
|
||||||
|
final client = GAuthClient.fromFile('credentials.json');
|
||||||
|
return GmailWrapper._internal(
|
||||||
|
gmailAddress: gmailAddress,
|
||||||
|
authClient: client,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// アプリパスワードなどの簡易認証は後実装
|
||||||
|
throw UnsupportedError('OAuth 方式でのみサポートされています');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GmailWrapper._internal({
|
||||||
|
required this._gmailAddress,
|
||||||
|
required GAuthClient? authClient,
|
||||||
|
}) : _authClient = authClient,
|
||||||
|
_gmail = authClient != null ? GmailService(authClient) : null;
|
||||||
|
|
||||||
|
/// ノード ID(BCC アドレス)を取得
|
||||||
|
String get gmailAddress => _gmailAddress;
|
||||||
|
|
||||||
|
/// チャットメッセージをノードに配送する
|
||||||
|
Future<void> sendMessage({
|
||||||
|
required String fromNode,
|
||||||
|
required String message,
|
||||||
|
String? toNode,
|
||||||
|
}) async {
|
||||||
|
if (_gmail == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// シンプルなテキストの送信(HTML のため簡易 escaping)
|
||||||
|
final body = base64Encode(utf8.encode(message));
|
||||||
|
|
||||||
|
await _gmail.users.messages.send(
|
||||||
|
userId: 'me',
|
||||||
|
body: EmailMessage(fromNode, message, toNode),
|
||||||
|
);
|
||||||
|
|
||||||
|
print('[Gmail] メッセージ送信完了 (from=$fromNode to=$toNode)');
|
||||||
|
} catch (e) {
|
||||||
|
print('[Gmail] 送信失敗:$e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ノードリストを取得(自己認識用)
|
||||||
|
Future<List<String>> getNodes() async {
|
||||||
|
// 現在接続可能なノードを返す(ハートビートの結果などから集約)
|
||||||
|
return []; // ここに母艦が管理するノードリストを読み込むロジック
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// メール送信用ダミーモデル(後実装で本格的な GmailService の構築へ)
|
||||||
|
class EmailMessage {
|
||||||
|
final String from;
|
||||||
|
final String body;
|
||||||
|
final String? to;
|
||||||
|
|
||||||
|
EmailMessage(this.from, this.body, this.to);
|
||||||
|
|
||||||
|
// ...実際のメールオブジェクト構築は Googleapis ライブラリに委譲...
|
||||||
|
}
|
||||||
121
lib/services/google/gmail_wrapper.dart
Normal file
121
lib/services/google/gmail_wrapper.dart
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Version: 1.0.0
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:googleapis_auth/google_auth.dart';
|
||||||
|
import 'package:googleapis/gmail/v1.dart';
|
||||||
|
|
||||||
|
/// Gmail API を介した同期用メールリレー
|
||||||
|
///
|
||||||
|
/// P2P 通信不要なノード識別システムを提供します。
|
||||||
|
/// BCC の Gmail アドレスをノードの一意キーとして使用。
|
||||||
|
class GmailWrapper {
|
||||||
|
final String _gmailAddress; // BCC 用 gmail アドレス(ノードキー)
|
||||||
|
final GAuthClient? _authClient; // OAuth 認証クライアント
|
||||||
|
final GmailService? _gmail;
|
||||||
|
|
||||||
|
/// 新しいインスタンスを作成
|
||||||
|
factory GmailWrapper({
|
||||||
|
required String gmailAddress,
|
||||||
|
bool useOAuth = true,
|
||||||
|
}) async {
|
||||||
|
if (useOAuth) {
|
||||||
|
try {
|
||||||
|
final client = GAuthClient.fromFile('credentials.json');
|
||||||
|
return GmailWrapper._internal(
|
||||||
|
gmailAddress: gmailAddress,
|
||||||
|
authClient: client,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// credentials.json がない場合、後で認証フローを実行できるように null を保持
|
||||||
|
print('[Gmail] credentials.json 未見。認証画面から開始してください');
|
||||||
|
return GmailWrapper._internal(
|
||||||
|
gmailAddress: gmailAddress,
|
||||||
|
authClient: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw UnsupportedError('OAuth 方式でのみサポートされています');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GmailWrapper._internal({
|
||||||
|
required this._gmailAddress,
|
||||||
|
GAuthClient? authClient,
|
||||||
|
}) : _authClient = authClient,
|
||||||
|
_gmail = authClient != null ? GmailService(authClient) : null;
|
||||||
|
|
||||||
|
/// ノード ID(BCC アドレス)を取得
|
||||||
|
String get gmailAddress => _gmailAddress;
|
||||||
|
|
||||||
|
/// 認証状態を確認
|
||||||
|
bool get isAuthorized => _authClient != null && _gmail != null;
|
||||||
|
|
||||||
|
/// チャットメッセージをノードに配送する
|
||||||
|
Future<void> sendMessage({
|
||||||
|
required String fromNode,
|
||||||
|
required String message,
|
||||||
|
String? toNode,
|
||||||
|
}) async {
|
||||||
|
if (_gmail == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// シンプルなテキストの送信(HTML のため簡易 escaping)
|
||||||
|
final body = base64Encode(utf8.encode(message));
|
||||||
|
|
||||||
|
final msg = EmailMessage(
|
||||||
|
subject: '[SalesAssist1] チャット:$fromNode',
|
||||||
|
to: toNode,
|
||||||
|
cc: null,
|
||||||
|
bcc: _gmailAddress, // BCC でノード識別
|
||||||
|
htmlBody: '<body><p>$message</p></body>',
|
||||||
|
);
|
||||||
|
|
||||||
|
final response = await _gmail.users.messages.send(
|
||||||
|
userId: 'me',
|
||||||
|
body: msg,
|
||||||
|
).root;
|
||||||
|
|
||||||
|
print('[Gmail] メッセージ送信完了 (from=$fromNode to=${toNode ?? 'BCC キー'})');
|
||||||
|
} catch (e) {
|
||||||
|
print('[Gmail] 送信失敗:$e');
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ノードリストを取得(自己認識用)
|
||||||
|
Future<List<String>> getNodes() async {
|
||||||
|
// 現在接続可能なノードを返す(ハートビートの結果などから集約)
|
||||||
|
return []; // ここに母艦が管理するノードリストを読み込むロジック
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 認証フローを起動(初回実行時)
|
||||||
|
Future<GAuthClient?> authenticate() async {
|
||||||
|
try {
|
||||||
|
final client = GAuthClient.fromFile('credentials.json');
|
||||||
|
print('[Gmail] 認証済みクライアント使用');
|
||||||
|
return client;
|
||||||
|
} catch (e) {
|
||||||
|
print('[Gmail] 認証フロー起動:$e');
|
||||||
|
// GoogleSignIn で認証画面をポップアップさせる処理(後実装)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// メール送信用モデル(Googleapis ライブラリの仕様に従う)
|
||||||
|
class EmailMessage {
|
||||||
|
String? subject;
|
||||||
|
List<String>? to;
|
||||||
|
String? cc;
|
||||||
|
String? bcc;
|
||||||
|
String? htmlBody;
|
||||||
|
|
||||||
|
EmailMessage({
|
||||||
|
this.subject,
|
||||||
|
this.to,
|
||||||
|
this.cc,
|
||||||
|
this.bcc,
|
||||||
|
this.htmlBody,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,10 @@ dependencies:
|
||||||
# Google エコシステム連携
|
# Google エコシステム連携
|
||||||
google_sign_in: ^6.1.0
|
google_sign_in: ^6.1.0
|
||||||
http: ^1.1.0
|
http: ^1.1.0
|
||||||
|
googleapis_auth: ^1.4.0
|
||||||
|
googleapis: ^12.0.0
|
||||||
|
googleapis_auth: ^1.5.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue