Javaとseleniumでteamsにメッセージを自動投稿する

今回はJavaとseleniumを用いて、teamsにメッセージを自動投稿する方法を書いていきます。
# 準備するもの
* Javaを動かせる環境
手順は割愛します。
* selenium
[seleniumをインストールする](https://www.selenium.dev/downloads/)
このURLから赤枠の場所をクリックします。
![install.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/b8749690-e10c-a507-c7cb-0f7887dce576.jpeg)
クリックするとzipファイルがインストールされるので、解凍して任意の場所にファイルを置きます。
このライブラリをJavaのプロジェクトに取り込むと完了です。


* 使用するブラウザのwebdriver
今回はGoogle Chromeを使用します。
[Chrome Driverをインストールする](https://chromedriver.chromium.org/downloads)
このURLからChrome Driverがインストールできますが、ChromeのバージョンとDriverのバージョンが一致するものをインストールしてください。
Chromeのバージョンを調べる方法は次の通りです。
1.「設定」をクリック
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/01a4ea19-27b1-72ae-2ad7-7cc7f1f6d92a.jpeg)
2.「Chromeについて」をクリック
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/98848c6b-9b9b-4963-76d2-a43769bb441c.jpeg)
3.赤枠の数字を確認する。今回は114です。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/fbe1ca0b-dae6-bdbb-9db2-5a18ebf4840c.jpeg)

Chromeのバージョンが分かったので、ドライバーをインストールします。
1.バージョンが114になっているものをクリックします。今回は２つがありますがどちらでも構いません。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/f677d359-e4c9-3dbe-7474-2b8f00b41d0f.jpeg)
2.インストール画面が表示されます。ご自身の環境にあったものをインストールしてください。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/4c7ac057-0d81-b951-6fb3-ce6e5d831841.jpeg)
3.zipファイルを解凍して任意の場所に置いてください。

これで、準備は完了です。

# 実際にソースを書いてメッセージを投稿する。
先にソースを全部載せます。細かい解説は後ほどしていきます。
```Java:Main.java
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.*;

public class Main {
	public static void timeWait(int second) {
		/* ミリ秒に直す */
		int milisecond = second * 1000;
		try {
			/* 処理を待つ */
			Thread.sleep(milisecond);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String args[]) {
		/* ログイン情報設定 */
		String url = "投稿したいチャットのURL";
		String mail = "メールアドレス";
		String passwd = "パスワード";

		/* Chrome Driver取得 */
		String path = "Chrome Driverのパス";
		System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, path);

		WebDriver driver = new ChromeDriver();
		driver.get(url);

		timeWait(3);

		try {
			/* メールアドレス入力してパスワード画面へ */
			WebElement element = driver.findElement(By.id("i0116"));
			element.sendKeys(mail);
			element = driver.findElement(By.id("idSIButton9"));
			element.click();

			/* パスワード入力してサインイン画面へ */
			timeWait(3);
			element = driver.findElement(By.id("i0118"));
			element.sendKeys(passwd);
			element = driver.findElement(By.id("idSIButton9"));
			element.click();

			/* サインインの状態を維持せず、チャット画面へ */
			timeWait(3);
			element = driver.findElement(By.id("idBtn_Back"));
			element.click();

			/* 通知オフボタンクリック */
			timeWait(15);
			element = driver.findElement(By.xpath("//*[@id=\"toast-container\"]/div/div/div[2]/div/button[2]"));
			element.click();

			/* iframeに切り替え */
			driver.switchTo().frame(0);
			timeWait(5);

			/* メッセージの入力＆送信 */
			element = driver.findElement(By.cssSelector(
					".ck.ck-content.ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline.ck-blurred"));
			element.sendKeys("テスト送信です");
			element = driver.findElement(By.cssSelector("button[data-tid=newMessageCommands-send]"));
			element.click();

			timeWait(3);
			driver.quit();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return;
	}
}
```

# 解説
それでは解説をしていきます。
## 準備
``` Java
/* ログイン情報設定 */
String url = "投稿したいチャットのURL";
String mail = "メールアドレス";
String passwd = "パスワード";
```
urlはブラウザから下記の画面を開き、そのURLをコピペしてください。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/a8eee9e3-f921-7d2a-50c5-fba03bc55e0a.jpeg)

mailとpasswdはログインするために必要なメールアドレスとパスワードになります。

``` Java
/* Chrome Driver取得 */
String path = "Web Driverのパス";
System.setProperty(ChromeDriverService.CHROME_DRIVER_EXE_PROPERTY, path);

WebDriver driver = new ChromeDriver();
driver.get(url);
```
pathはインストールしたWebDriverのパスになります。
この処理で、Chromeからサインイン画面を開きます。

## temasにログインする
``` Java
timeWait(3);

try {
    /* メールアドレス入力してパスワード画面へ */
    WebElement element = driver.findElement(By.id("i0116"));
    element.sendKeys(mail);
    element = driver.findElement(By.id("idSIButton9"));
    element.click();
```
この処理はサインイン画面でメールアドレスを入力して、次へを押す処理となります。
findElementの中身はHTMLのidを取得しています。
最初なので丁寧に見ていきます。
まずは、サインイン画面で右クリックをして「検証」をクリックしてください。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/c1b35e22-2a6a-5f9c-f5d9-7d383f5d895f.jpeg)
すると、こんな画面が表示します。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/46d463b4-e5e1-ec94-5c8a-08bd9dbf4959.jpeg)
これは、この画面のHTMLとなります。
さらにメールを入力する箇所でもう一度右クリック→検証を行ってください。
すると、このような画面が表示されます。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/a22b9ade-40c2-b06e-f934-d778b1ca3cca.jpeg)
HTMLの画面の表示を大きくしています。
これはメールアドレス入力欄のHTML文となります。
ここで「id="0116"」とあります。
HTMLのidは重複を許さないため、idからメールアドレス入力欄であることが分かるという仕組みとなります。
つまり、idがi0116の要素を取ってこいという命令をfindElementが行います。
次にelement.sendKeys(mail)はsendKeysに指定した引数を入力します。これでメールアドレス欄にメールアドレスが入力されます。

element = driver.findElement(By.id("idSIButton9"))は先ほどと同様にidがidSIButton9の要素を取って来いという命令です。このidは「次へ」ボタンを表します。
element.click()でボタンをクリックする命令を送ります。

これでパスワード入力画面に遷移しますが、一つ注意点があります。
それはtimeWaitで処理を一時中断していることです。
ここで、処理を止めないとサインイン画面が表示される前に、メールアドレス入力欄を取得しようします。
それを防ぐために画面を遷移する際は処理を一旦中止することを心がけましょう。

解説が長くなったので、もう一度ソースコードとその解説を記載します。
``` Java
/* サインイン画面が表示されるのを待つ */
timeWait(3);

try {
    /* メールアドレス入力欄の要素を取得する */
    WebElement element = driver.findElement(By.id("i0116"));
    /* mailの値をメールアドレス入力欄に入力する */
    element.sendKeys(mail);
    /* 次へボタンの要素を取得する */
    element = driver.findElement(By.id("idSIButton9"));
    /* 次へボタンをクリックする */
    element.click();
```

後は、ひたすら画面に沿って処理をしていきます。
サインイン画面で次へボタンを押すとをパスワード画面が表示されます。
この画面ですね。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/b6306524-5c9a-a973-0334-bd079d314a4b.jpeg)
この画面の処理は次の通りです。
``` Java
/* パスワード画面が表示されるのを待つ */
timeWait(3);
/* パスワード入力欄の要素を取得する */
element = driver.findElement(By.id("i0118"));
/* passwdの値をパスワード入力欄に入力する */
element.sendKeys(passwd);
/* 次へボタンの要素を取得する */
element = driver.findElement(By.id("idSIButton9"));
/* 次へボタンをクリックする */
element.click();
```

次はサインインを維持するかどうかの画面ですね。
この画面ではいいえをクリックします。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/e4a65c79-054b-99b7-9276-1cee41b0054f.jpeg)
``` Java
/* サインイン維持画面が表示されるのを待つ */
timeWait(3);
/* いいえボタンの要素を取得する */
element = driver.findElement(By.id("idBtn_Back"));
/* いいえボタンをクリックする */
element.click();
```
## メッセージを自動投稿する
ここまでの処理が終わるとチャット画面が表示されます。
メッセージを入力する前にまずは右下の通知画面を消します。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/3c8be6e8-8841-2104-74f6-204c2a0795d4.jpeg)

```Java
/* 通知オフボタンクリック */
/* チャット画面が表示されるのを待つ */
timeWait(15);
/* 通知の閉じるボタンの要素を取得する */
element = driver.findElement(By.xpath("//*[@id=\"toast-container\"]/div/div/div[2]/div/button[2]"));
/* 閉じるボタンクリックする */
element.click();
```

ではいよいよメッセージを入力して送信します。
``` Java
/* iframeに切り替え */
driver.switchTo().frame(0);
timeWait(5);

/* メッセージ入力欄の要素を取得する */
element = driver.findElement(By.cssSelector(".ck.ck-content.ck-editor__editable.ck-rounded-corners.ck-editor__editable_inline.ck-blurred"));
/* メッセージ入力欄に「テスト送信です」と入力する */
element.sendKeys("テスト送信です");
/* 送信ボタンの要素を取得する */
element = driver.findElement(By.cssSelector("button[data-tid=newMessageCommands-send]"));
/* 送信ボタンをクリックする */
element.click();

timeWait(3);

/* ブラウザを閉じる */
driver.quit();
```
ここでiframeを切り替えることに注意してください。
iframeとはHTML内に別のHTMLを入れ子にすることです。
操作する画面を親画面から子画面に切り替えるといった方がイメージがつきやすいでしょうか。
見た目は１つの画面ですが、teamsは画面上部の検索欄が親画面、それ以外はすべて子画面のような作りになっているようです。
今回は子画面を操作するので、それに切り替えるということをdriver.switchTo().frame(0)で行っています。

# プログラムの実行
それでは作ったプログラムを実行してみましょう。
スクショしかないので、手動入力しただろと思われてもおかしくないですが、ちゃんと自動で入力しています。
![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314839/f8e314b5-7709-96d7-db7b-eb14b6eb5b2b.jpeg)
「テスト送信です」というメッセージを送ることが出来ました。
これで動作確認もOKです。

# おまけ
## 完全自動でメッセージ送信する。
ここまで、自動で実行する処理を作りましたが、プログラムを自分で起動しないといけないのでは意味がないですね。
下記の手順で完全に自動で実行することが出来ます。
1. ソースからjarファイルを作成する。
1. Windowsの場合、タスクスケジューラから指定した日時、時間にjarファイルを自動で実行するようにする。
これで、毎日の挨拶のようなルーティン的なものは完全自動でメッセージを送信できるようになります。
単純作業とはこれでおさらばですね。

## ヘッドレスモードを活用する
ヘッドレスモードとは処理を表に出さず、裏でプログラムを動作させることです。
タスクスケジューラに登録すると、ほかの作業中に急にChromeが立ち上がり、teamsにメッセージ送信するため、ややストレスになります。それを回避するための方法です。
ヘッドレスモードはソースを修正することで、実現できます。
以下の２か所を修正してください。
``` Java
/* WebDriver driver = new ChromeDriver();*/

/* ヘッドレスモードで起動 */
WebDriver driver = new ChromeDriver(new ChromeOptions().addArguments("--headless"));
```
``` Java
/* チャット画面の通知オフを閉じるボタンクリック処理は不要 */
/* element = driver.findElement(By.xpath("//*[@id=\"toast-container\"]/div/div/div[2]/div/button[2]"));
element.click();*/ 
```
ヘッドレスモードにした場合は、通知画面が表示されないようなので、こちらの処理は不要となります。
削除しないと要素が取得できず、エラーになります。

これで、処理を画面に出すことなく、Teamsにメッセージを送信することが出来ます。

# まとめ
今回は、Javaとseleniumでteamsにメッセージを自動で送信する方法をご紹介しました。
他にも条件はありますが、以下のやり方があるので興味がある方は調べてみてください。
1. Power Automateを使う→有料ソフトのため、コストがかかる
1. incoming webhookを使う→チームのチャットにしか送れない。自分のメモや個人間のやり取りは向いていない

今回は汎用的に使えるようしたかったため、スクレイピングを使用しました。
スクレイピングはJavaよりPythonの方が記事が豊富であるため、Pythonを使って作った方が困ったときに解決しやすいと思います。