194 lines
No EOL
6.1 KiB
Dart
194 lines
No EOL
6.1 KiB
Dart
// Version: 1.0.0
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:google_sign_in/google_sign_in.dart';
|
||
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 アドレス(ノードキー)
|
||
GAuthClient? _authClient; // OAuth 認証クライアント
|
||
final GmailService? _gmail; // Gmail API サービス
|
||
final GoogleSignIn _signIn; // GoogleSignIn インスタンス
|
||
|
||
/// 新しいインスタンスを作成
|
||
factory GmailWrapper({
|
||
required String gmailAddress,
|
||
bool useOAuth = true,
|
||
}) {
|
||
if (useOAuth) {
|
||
print('[Gmail] OAuth 認証モード。GoogleSignIn でアカウント選択を行います');
|
||
final signIn = GoogleSignIn(
|
||
scopes: [
|
||
'https://www.googleapis.com/auth/gmail.readonly',
|
||
'https://www.googleapis.com/auth/calendar.readonly',
|
||
'https://www.googleapis.com/auth/drive.readonly',
|
||
'https://www.googleapis.com/auth/spreadsheets.readonly',
|
||
],
|
||
);
|
||
return GmailWrapper._internal(
|
||
gmailAddress: gmailAddress,
|
||
signIn: signIn,
|
||
);
|
||
} else {
|
||
throw UnsupportedError('OAuth 方式でのみサポートされています');
|
||
}
|
||
}
|
||
|
||
GmailWrapper._internal({
|
||
required this._gmailAddress,
|
||
required GoogleSignIn signIn,
|
||
}) : _signIn = signIn,
|
||
_authClient = null,
|
||
_gmail = null;
|
||
|
||
/// ノード ID(BCC アドレス)を取得
|
||
String get gmailAddress => _gmailAddress;
|
||
|
||
/// 認証状態を確認
|
||
bool get isAuthorized => _gmail != null;
|
||
|
||
/// GoogleSignIn インスタンスを取得
|
||
GoogleSignIn get signInInstance => _signIn;
|
||
|
||
/// 現在選択中のアカウントのメールアドレスを取得
|
||
String? get currentAccountEmail => _signIn.currentUser?.email;
|
||
|
||
/// 認証された Google アカウント情報を取得(初回実行時は null)
|
||
GoogleSignInAccount? get currentUser => _signIn.currentUser;
|
||
|
||
/// チャットメッセージをノードに配送する
|
||
Future<void> sendMessage({
|
||
required String fromNode,
|
||
required String message,
|
||
String? toNode,
|
||
}) async {
|
||
if (_gmail == null) return;
|
||
|
||
try {
|
||
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 []; // ここに母艦が管理するノードリストを読み込むロジック
|
||
}
|
||
|
||
/// OAuth 認証フローを実行(初回またはアカウント切り替え時)
|
||
Future<GoogleSignInAccount?> authenticate() async {
|
||
try {
|
||
final account = await _signIn.signIn();
|
||
if (account != null) {
|
||
print('[Gmail] 認証成功:${account.email}');
|
||
return account;
|
||
} else {
|
||
throw Exception('ユーザーが認証をキャンセルしました');
|
||
}
|
||
} catch (e) {
|
||
print('[Gmail] 認証失敗:$e');
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
/// 現在のアカウントでログアウト
|
||
Future<void> logout() async {
|
||
try {
|
||
await _signIn.signOut();
|
||
print('[Gmail] ログアウト済み');
|
||
} catch (e) {
|
||
print('[Gmail] ログアウト失敗:$e');
|
||
}
|
||
}
|
||
|
||
/// アカウント切り替え(複数アカウントを持つ場合)
|
||
Future<GoogleSignInAccount?> switchAccount() async {
|
||
try {
|
||
final account = await _signIn.signIn();
|
||
if (account != null) {
|
||
print('[Gmail] アカウント切り替え:${account.email}');
|
||
return account;
|
||
} else {
|
||
throw Exception('ユーザーが認証をキャンセルしました');
|
||
}
|
||
} catch (e) {
|
||
print('[Gmail] 切り替え失敗:$e');
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
/// Gmail API サービスインスタンスを取得・初期化
|
||
Future<void> initializeApi() async {
|
||
if (_authClient == null || _gmail != null) return;
|
||
|
||
try {
|
||
// credentials.json が存在する場合、OAuth2 認証を作成
|
||
if (await kIsWeb || defaultTargetPlatform == Android) {
|
||
// Android では credentials.json を使用せず、GoogleSignIn のトークンを使用
|
||
print('[Gmail] GoogleSignIn のアクセストークンを使用');
|
||
|
||
// GAuthClient を生成し、GmailService を初期化
|
||
final client = GAuthClient.withCredentials(
|
||
'google_sign_in_credentials.json', // 後実装:SDK が生成するファイル名
|
||
);
|
||
_authClient = client;
|
||
_gmail = GmailService(client);
|
||
} else {
|
||
print('[Gmail] Web/iOS モード。GoogleSignIn で管理');
|
||
}
|
||
} catch (e) {
|
||
print('[Gmail] API 初期化失敗:$e');
|
||
rethrow;
|
||
}
|
||
}
|
||
|
||
/// アクセストークンを取得(GoogleSignIn から)
|
||
Future<String?> getToken() async {
|
||
if (_signIn.currentUser == null) return null;
|
||
|
||
try {
|
||
final auth = await _signIn.authenticator;
|
||
return auth.accessToken.toString();
|
||
} catch (e) {
|
||
print('[Gmail] トークン取得失敗:$e');
|
||
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,
|
||
});
|
||
} |