SOFTELメモ Developer's blog

会社概要 ブログ 調査依頼 社員募集 ...

【php】 Google Groups Migration API を使う

問題

メーリングリストの過去の履歴を Groups Migration API を使って、Google グループに移行したい。

(参考) https://developers.google.com/admin-sdk/groups-migration/v1/quickstart/php

Google groups logo

答え

メールソフトで受信したメールや、メーリングリストのシステムに残った履歴などがあれば可能。

APIへは rfc822 形式で送れとのことなので、qmail、postfixなどのMaildir内にあるメールのファイル、もしくは手元のメールソフトで受信したメールを適当なディレクトリにエクスポートしたものがあれば、それらをループしてアップロードしていけばよい。

ただし、元のメールが悪いとたまに文字化けする。

1、Google Apps Groups Migration API を使えるようにする

管理画面でAPI利用を有効にしてクライアントID(JSON形式)を取得して、client_secret.json などのファイル名で保存しておく。

  1. Use this wizard to create or select a project in the Google Developers Console and automatically turn on the API. Click Continue, then Go to credentials.
  2. On the Add credentials to your project page, click the Cancel button.
  3. At the top of the page, select the OAuth consent screen tab. Select an Email address, enter a Product name if not already set, and click the Save button.
  4. Select the Credentials tab, click the Create credentials button and select OAuth client ID.
  5. Select the application type Other, enter the name “Google Apps Groups Migration API Quickstart”, and click the Create button.
  6. Click OK to dismiss the resulting dialog.
  7. Click the file_download (Download JSON) button to the right of the client ID.
  8. Move this file to your working directory and rename it client_secret.json.

https://developers.google.com/admin-sdk/groups-migration/v1/quickstart/php

2、ライブラリを用意するために、composer(パッケージ管理)を用意する

(適当なディレクトリを作って、そこで実行)
wget https://getcomposer.org/installer
php installer
rm installer
(これで composer.phar ができている)

適当なディレクトリに composer.phar が用意できたら、Google Client Library(と依存関係にあるライブラリ)を取得する。

php composer.phar require google/apiclient:^2.0
(これでライブラリ一式がダウンロードされる)

3、プログラムを作って実行する

以下はプログラムの例。

/example/ は環境に応じて適宜読み替えられたし。

たまにエラーが起きるので、503エラーはリトライするようにした。

<?php

//composerをインストールした場所を指定
require '/example/composer/vendor/autoload.php';

//アプリケーションにつけた名前
define('APPLICATION_NAME', 'Google Apps Groups Migration API Quickstart');
//アクセスキー、リフレッシュトークンなどを保存するファイル名
define('CREDENTIALS_PATH', '/example/.credentials');
//管理画面からダウンロードした client ID を保存したファイルを指定
define('CLIENT_SECRET_PATH', '/example/client_secret.json');
//権限設定
define('SCOPES', implode(' ', array(
	Google_Service_GroupsMigration::APPS_GROUPS_MIGRATION)
));

//履歴取り込み先のグループの名前@組織のドメイン
$groupId = 'test@example.com';

//取り込みたいメールのファイルの一覧
//Maildirからファイル一覧を取得するなどして作る
//古い順に並べておくと、スレッドの親子関係や、元のメールと返信のメールがうまく取り込まれるかも
$entries = array(
	'/home/vpopmain/domains/example.com/test/Maildir/cur/1481177359.8416.example.com,S=1:2,',
	'/home/vpopmain/domains/example.com/test/Maildir/cur/1481177360.8417.example.com,S=2:2,',
	'/home/vpopmain/domains/example.com/test/Maildir/cur/1481177361.8418.example.com,S=3:2,',
	'/home/vpopmain/domains/example.com/test/Maildir/cur/1481177362.8419.example.com,S=4:2,',
	//...
	//...
);

/**
 * Returns an authorized API client.
 * @return Google_Client the authorized client object
 */
function getClient()
{
	$client = new Google_Client();
	$client->setApplicationName(APPLICATION_NAME);
	$client->setScopes(SCOPES);
	$client->setAuthConfigFile(CLIENT_SECRET_PATH);
	$client->setAccessType('offline');

	// Load previously authorized credentials from a file.
	$credentialsPath = CREDENTIALS_PATH;
	if (file_exists($credentialsPath)) {
		$accessToken = file_get_contents($credentialsPath);
	} else {
		// Request authorization from the user.
		$authUrl = $client->createAuthUrl();
		printf("Open the following link in your browser:\n%s\n", $authUrl);
		print 'Enter verification code: ';
		$authCode = trim(fgets(STDIN));

		// Exchange authorization code for an access token.
		$accessToken = $client->authenticate($authCode);

		// Store the credentials to disk.
		if(!file_exists(dirname($credentialsPath))) {
			mkdir(dirname($credentialsPath), 0700, true);
		}
		file_put_contents($credentialsPath, json_encode($accessToken));
		printf("Credentials saved to %s\n", $credentialsPath);
	}
	$client->setAccessToken($accessToken);

	// Refresh the token if it's expired.
	if ($client->isAccessTokenExpired()) {
		$refreshToken = $client->getRefreshToken();
		$client->refreshToken($refreshToken);
		$newAccessToken = $client->getAccessToken();
		$newAccessToken['refresh_token'] = $refreshToken;
		file_put_contents($credentialsPath, json_encode($newAccessToken));
	}
	return $client;
}

while ($entries) {
	try {
		$entry = array_shift($entries);

		$client = getClient();
		$service = new Google_Service_GroupsMigration($client);
		$optParams = array(
				'data' => file_get_contents($entry),
				'mimeType' => 'message/rfc822',
				'uploadType' => 'media',
			);
		$result = $service->archive->insert($groupId, $optParams);

		//成功したかどうか見たかったら戻り値を見ることは可能
		if ($result->getResponseCode() == 'SUCCESS') {
			//例えばどこまで処理が進んだかファイルに書き出しておくなど
		}
	} catch (Google_Service_Exception $e) {
		if ($e->getCode() == 503) {
			//たまにエラーが発生するので、その時は $entries に戻して、もう一度ループしてもらう
			array_unshift($entries, $entry);
		} else {
			//503以外のエラーは見たことがないがリトライしても処理できるかどうかわからないので中断
			throw $e;
		}
	}
}

関連するメモ

コメント