non vorrei lavorare

ブログ名の通りです。javascript three.js mruby rust OCaml golang julialang blender

アレクサで「iPhoneを探す」をpuppeteerを使って出来るようにした

おはようございます。卒園式のDVDが完成し、その上映会を行い、久しぶりに長男の保育園の皆さんと再会し、寿退職された担任の先生、0歳児から担当してくれて、昨日はお泊り保育明けの先生も来てくださり、子供たちも再開を喜び、上映会も楽しめたようです。@kjunichiです。

既存方法の問題点

実は、このアレクサで「iPhoneを探す」は、IFTTT経由でiPhoneに電話をかけることで実現する方法がずいぶん前からあったのですが、 多分これは、日本ではできそうではないですし、電話代がかかる心配もあって見送っていました。

もう一つ

dev.classmethod.jp

に紹介されている方法ですが、多分これも、2要素認証で運用していると面倒そうなことが、 利用しているiCloudライブラリpyicloudGithubページを斜め読みする限りでは伺えました。

puppeteerで実現した

今回取った方法は、最近2要素認証でもブラウザ経由でiPhoneを探すのリンクに直接アクセスすると、自分の環境では、 2要素認証を回避してアクセスできるので、これで行けるかも?と思い試した。

なお、puppeteerの起動は、IFTTTでAlexaのトリガーを仕掛けて、Dropbox経由でローカルのPCにファイルを作成し、 PC側でこのファイルの作成を監視するとこで実現した。

f:id:kjw_junichi:20180708101311p:plain

いきなりAppleの嫌がらせにあうの巻

何故か、iframeで認証ページ、iPhoneを探すページとも組まれており、devtoolsで要素名を調べてもpageオブジェクト経由では アクセスできないようになっていました。

以下のページが大変参考になりました。

qiita.com

また、そもそも今回puppeteerを始めて使ったので、全般的なコードは公式ページの他、以下のページが参考になりました。

qiita.com

成果物

TARGET_ID、TARGET_PWDiCloudのアカウントを入れて、TARGET_PHONE_NAME は対象のiPhoneに付けている名前を入れる。 そして、pathはDropboxのパスを入れることで、動くはず。

const fs=require('fs')
const puppeteer = require('puppeteer');

const TARGET_ID = "test@mac.local";
const TARGET_PWD = "password";
const TARGET_PHONE_NAME = 'iphonename';
const path="D:/Dropbox/Amazon Alexa";

const findmyiphone = () => {
(async () => {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();
  page.goto('https://www.icloud.com/#find');
  await page.waitFor(12000);
  const dimensions = await page.evaluate(() => {
    return {
      text: document.body.innerHTML
    };
  });

  //console.log('Dimensions:', dimensions);
  //await page.type("input#account_name_text_field", "input");

  //await page.screenshot({path: 'example.png'});

  const frame = await page.frames().find(f => f.name() === 'auth-frame');
  await frame.type("input#account_name_text_field", TARGET_ID);
  const tform = await frame.evaluate(() => {
    return {
      text: document.body.innerHTML
    };
  });
  await frame.click('i');
  await frame.waitFor(1000);
  //await frame.waitFor('input#password_text_field',{timeout: 1000});
  await frame.type("input#password_text_field", TARGET_PWD);
  await frame.click('i');
  await page.waitFor(12000);
  const frame2 = await page.frames().find(f => f.name() === 'find');
  await frame2.click('label[id="sc2348-label"]')
  await frame2.click(`div[title="${TARGET_PHONE_NAME}"]`)
  await frame2.click('div[title="このiPhoneでサウンドを再生"]')
  //console.log(tform);
  await browser.close();
})();
}

fs.watch(path, (event, filename) => {
  if(filename&& event =="rename") {
      if(filename.startsWith("AmazonAlexa_findmyiphone")) {
          console.log("Find my iphone!")
          findmyiphone();
      }
  }
  console.log(event,filename)
});

あとがき

puppeteerでGUI非表示でやらせたかったものの、GUI表示しないと、エラーとなり、 まぁ、自分の用途では何が何でも非表示する必要もなかったので、これは放置してます。

また、Dropboxでのファイル作成もWindowsではIFTTTのデフォルトの日付入りだとファイルが作成されないので、 固定のファイル名にする必要があった。

関連記事

11年前の記事

6年前の記事

2年前の記事

1年前の記事