Selenium経由でSlackのDMを取得･送信･スクショする

ブラウザの操作を自動化するライブラリ「[**Selenium**](https://www.selenium.dev/documentation/)」を使って[**Slack**](https://slack.com/intl/ja-jp/)へ自動でログインし、特定のメンバーのDM欄からテキストを自動で取得・送信したり、スクリーンショットを保存したりするプログラムの一例です。

>先日の投稿の上位互換プログラムです。

https://qiita.com/h-nabata/items/96d6a9068cb7c6ad26ae

```py:auto-dm.py
# （Selenium 4.1.0 で動作確認済み）
# webdriver は使用しているChromeのバージョンに合わせて以下のページから取得する。
# https://chromedriver.chromium.org/downloads

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome import service as fs
import time
import sys
import os

# パラメータ
slackurl    = 'sample.slack.com'        # Slack URL
mailaddress = 'sample@gmail.com'        # 登録済みメールアドレス
mypassword  = 'abrakadabra'             # ログインパスワード
waittime    = 10.0                      # (sec.) 待機時間 (通信速度 & PCスペックに応じて調整)
logintime   = 10.0                      # (sec.) ログインを維持する時間 (適宜調整)
driverpath  = 'C:/Users/[user名]/Downloads/chromedriver_win32/chromedriver.exe' # webdriver が置かれている path を指定
workpath    = 'C:/Users/[user名]/Downloads/'                                    # 取得した画像などの保存先を指定
url         = 'https://app.slack.com/client/XXXYYYZZZ/AAABBBCCC'                # 目的地となる Slack WorkSpace のURL
recipient   = '山田'    # 相手(DMの受信者)のアカウント名
mymessage   = 'Yes My Darkness!'    # DMの内容
order       = 1  # 何番目に最新のメッセージを取得するか
dm_mode     = 0  # 0 -> 何もしない  //  1 -> メッセージを送信する
ss_mode     = 1  # 0 -> 何もしない  //  1 -> 最新のメッセージのスクリーンショットを保存  //  2 -> DM欄全体のスクリーンショットを保存


# フォルダの存在確認
if os.path.exists(workpath) == False:
    print('% Error: The "workpath" is invalid...')
    sys.exit()

# webdriverの存在確認
if os.path.exists(driverpath) == False:
    print('% Error: The "driverpath" is invalid...')
    sys.exit()

try:
    # 自動ログイン
    try:
        # Webページにアクセス
        chrome_service = fs.Service(executable_path=driverpath)
        driver  = webdriver.Chrome(service=chrome_service)
        driver.maximize_window()  #全画面表示にする (オプション)
        driver.get(url)
        driver.implicitly_wait(waittime)  # 遷移待ち

        # Slack WorkSpaceの選択
        ch_element = driver.find_element(By.ID, 'domain')
        ch_element.send_keys(slackurl)
        ch_element.send_keys(Keys.ENTER)
        driver.implicitly_wait(waittime)  # 遷移待ち

        # email, password の自動入力
        mail_element = driver.find_element(By.ID, 'email')
        mail_element.send_keys(mailaddress)
        driver.implicitly_wait(waittime)  # 待機 (オプション)
        password_element = driver.find_element(By.ID, 'password')
        password_element.send_keys(mypassword)
        password_element.send_keys(Keys.ENTER)
        driver.implicitly_wait(waittime)  # 遷移待ち (少し長め)

        # 自動ログインに成功
        print('% Successfully logged in:')
        url     = driver.current_url    # URLを取得する (オプション)
        title   = driver.title          # タイトルを取得する (オプション)
        print("% URL       :", url, )
        print("% title     :", title)

    except:
        # ログインに失敗した場合はセッションを終了する
        driver.quit()
        print("% Error: Your login has been failed...")
        sys.exit()

    # 送信先(recipient)のDMを開く
    recipient_list = driver.find_elements(By.CSS_SELECTOR, '[data-qa="channel_sidebar_name_' + recipient + '"]')
    if len(recipient_list) > 0:    # DMリストに名前が表示されている場合
        print("% DM is showing in the current list.")
        driver.find_element(By.CSS_SELECTOR, '[data-qa="channel_sidebar_name_' + recipient + '"]').click()
        driver.implicitly_wait(waittime)  # 遷移待ち
    else:    # DMリストに名前が表示されていない場合
        print("% DM is NOT showing in the current list.")
        searchbox_element = driver.find_element(By.CSS_SELECTOR, '[class="c-button-unstyled p-top_nav__search p-top_nav__search--windows-linux"]')
        searchbox_element.send_keys("@" + recipient)
        driver.implicitly_wait(waittime)  # 待機 (オプション)
        driver.find_element(By.CSS_SELECTOR, '[class="c-truncate c-truncate--break_words"]').click()
        driver.implicitly_wait(waittime)  # 待機 (オプション)

    # DMの要素をリストとして取得
    message_element_list = driver.find_elements(By.CSS_SELECTOR, '[class="c-message_kit__gutter"]')  # メッセージの要素を特定
    messagenum = -1*order
    target_massage = message_element_list[messagenum]  # 最新のメッセージをテキストで取得
    print("% sender    :", target_massage.find_element(By.CSS_SELECTOR, '[class="c-message__sender c-message_kit__sender"]').text) # 最新のメッセージの送信者
    print("% timestamp :", target_massage.find_element(By.CSS_SELECTOR, '[class="c-timestamp__label"]').text) # 最新のメッセージの送信時刻
    if target_massage.find_elements(By.CSS_SELECTOR, '[class="p-rich_text_section"]'):
        print("% message   :", target_massage.find_element(By.CSS_SELECTOR, '[class="p-rich_text_section"]').text, sep="\n") # 最新のメッセージの本文
    else:
        print("% (There is no text in the message.)")  # テキストを含まないメッセージの場合
        ss_mode = 1  # スクリーンショットの保存を強制する (オプション)

    # メッセージを送る (オプション)
    if dm_mode == 1:
        post_element = driver.find_element(By.CSS_SELECTOR, '[class="ql-editor ql-blank"]')
        post_element.click()
        driver.implicitly_wait(waittime)  # 待機 (オプション)
        post_element.send_keys(mymessage)  # メッセージ入力
        post_element.send_keys(Keys.ENTER)
        driver.implicitly_wait(waittime)  # 待機 (オプション)
        driver.find_element(By.CSS_SELECTOR, '[class="c-icon c-icon--paperplane-filled"]').click()
        print("% Your message was successfully sent.")

    # DMの画像を取得する (オプション)
    if ss_mode == 1:
        time.sleep(waittime)  # 強制的な待機 (オプション)
        png = message_element_list[messagenum].screenshot_as_png  # 最新のメッセージのみをpngで取得
        with open(workpath + 'image_from_slack.png', 'wb') as f:
            f.write(png)  # 画像の書き出し
            print("% The image of the latest message has been saved successfully.")
    elif ss_mode == 2:
        time.sleep(waittime)  # 強制的な待機 (オプション)
        dmfield_element = driver.find_elements(By.CSS_SELECTOR, '[class="p-workspace__primary_view"]')  # DM欄の要素を特定
        png = dmfield_element[messagenum].screenshot_as_png  #DM欄をpngで取得
        with open(workpath + 'image_from_slack.png', 'wb') as f:
            f.write(png)  # 画像の書き出し
            print("% The image of the messages being displayed has been saved successfully.")


    time.sleep(logintime)    # 任意時間ログイン状態を維持

finally:
    driver.quit()    # セッションの終了
    print("% Done.")
    sys.exit()

```

>セキュリティの問題が生じない範囲で適宜改変してご利用下さい。
Slack内のクラス名などは予告なく変更されることがありますので、継続的に使用する場合は適宜保守して下さい。