Googleスプレッドシートを画像としてDiscordBotに送信させる

# 目的

discord自体にテーブルの文字装飾がなく、
テーブルとしてスプレッドシートの情報を共有するには画像としてアップロードするしかありません。

手動アップロードするにもWindowsにはスクリーンショットを画像として保存する標準機能はないし、
Macだとしても一度ローカルに画像保存してからなので手間です。

なのでDiscordのBotを利用してアップロードしてもらいます。

言語はPythonを使用しています。

# 流れ

![名称未設定ファイル (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257872/19d6c762-3f5c-88ee-fd96-dc68027c385d.png)

スプレッドシートがPNGとしてエクスポートしてくれるなら簡単にできますが、
残念ながらその機能はないため`pdf2image`というpythonの神ライブラリを使用して変換します。

スプレッドシートから特定の呪文を呟かなくてもBotへ直接postする方法が簡単だと思いますが、
実装していたBotに特定の呪文に対して何かをするという機能があったためそれを利用しました。

# 手順

## 1. 事前準備

事前にpython3を導入しておきます。

pythonインストールしている方は3系か確認してください。

```bash
$ python3 -V
Python 3.7.3
```

## 2. Google側の準備
pythonでスプレッドシートを操作するためにはAPI有効化とOAuthの設定が必要です。
こちらのわかりやすい記事を参考にさせていただきました。

今回はpython3を利用するためライブラリのインストール時は`pip3`を使用してください。

[【もう迷わない】Pythonでスプレッドシートに読み書きする初期設定まとめ](https://tanuhack.com/python/operate-spreadsheet/)




## 3. DiscordのWebhook設定
1. Discordのサーバー設定を開き、「ウェブフック」の[ウェブフックを作成]をクリックします。![スクリーンショット 2019-08-13 11.53.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257872/c46ed2b0-f14a-8f23-c131-d02e79f71b40.png)
2. webhookの設定をして作成します。（「ウェブフックURL」はメモ帳にでもコピーしておく）<img width="472" alt="スクリーンショット 2019-08-13 12.01.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257872/d9c96a4d-e7cb-2b60-622d-f5fc19355ef2.png">


## 4. GASを書く
スプレッドシートの[ツール]-[スクリプトエディタ]からコードを書くことができます。

トリガーとなる呪文「get_image」を呟くだけなので特に難しいことはしていません。

自身の設定に合うようにコード修正してください。

```javascript:post.gs
function post() {
  var text = "get_image";
  postDiscord(text);
}
 
function postDiscord(text,url) {
   
  var url="<DiscordのWebhookURL>";
  var token="<https://discordapp.com/api/webhooks/＜サーバーID＞/ここの部分>";
  var channel="<Webhookで設定したチャンネル>";
  var username="<Webhookの名前>";
  var parse = "full";
  var method= "post";
   
  var jsonData={
    "token":token,
    "channel":channel,
    "username":username,
    "content":text,
    "parse":parse,
  }
   
  var options={
    "method":method,
    "headers":{"Content-type":"application/json"},
    "payload":JSON.stringify(jsonData),
    "muteHttpExceptions":true
  };
   
  UrlFetchApp.fetch(url,options);
}
```

## 5. トリガーボタンを作成

書いたコードをいちいち実行するのも手間なので、シート内にボタンを配置します。

[挿入]-[図形描画]からボタンを作成して、作成された図形にスクリプトを割り当てます。
<img width="229" alt="スクリーンショット 2019-08-13 12.18.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257872/db98651e-a21a-805c-e4b2-d9182dad7903.png">

実行する関数名が`post`なので、今回は割り当てるスクリプトは`post`にします。


## 6. bot導入

偉大な先人の方の記事を参考にしました。

[Discord Botアカウント初期設定ガイド for Developer](https://qiita.com/1ntegrale9/items/cb285053f2fa5d0cccdf)

## 7. コード作成準備

`discord.py`をインストールします。
インストール方法は公式に記載されています。
https://discordpy.readthedocs.io/ja/latest/intro.html#installing

pdfを画像に変換するための`pdf2image`もインストールします。

```bash
$ python3 -m pip install -U discord.py
$ pip3 install pdf2image
```

## 8. コード作成

コードは以下のように書きました。（本記事と関係ない機能は消しています）

使用する場合は環境にあうようにコード修正してください。

```python:post.py
import discord
import urllib.request
import json
import re
import gspread
from pdf2image import convert_from_path
from oauth2client.service_account import ServiceAccountCredentials

client = discord.Client()

###################################
# 定義
###################################
#spreadsheet
spreadsheet_url = "<スプレッドシートのURL>"

#2つのAPIを記述しないとリフレッシュトークンを3600秒毎に発行し続けなければならない
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']

#認証情報設定（ファイルはgoogleの認証情報をリネーム）
credentials = ServiceAccountCredentials.from_json_keyfile_name('./credentials.json', scope)

#OAuth2の資格情報を使用してGoogle APIにログインします。
gc = gspread.authorize(credentials)

###################################
# 実行部分
###################################
@client.event
async def on_message(message):
  if message.author != client.user:
    # get_imageでスプレッドシートの画像取得実行
    if "get_image" in message.content:
      #pdf取得
      pdf_export_url = spreadsheet_url + "/export?format=pdf&gid=<シートのGID>&range=A1:D14&portrait=false&size=8&fitw=true&vertical_alignment=top&horizontal_alignment=CENTER&scale=3"
      pdf_name = "output.pdf"
      urllib.request.urlretrieve(pdf_export_url, pdf_name)

      #画像変換
      image = convert_from_path(pdf_name)
      image[0].save('output.png', 'png')

      #変換した画像ファイル送信
      await client.send_file(message.channel, 'output.png')

client.run('<BOTのトークンID>')
```

### コードの補足

スプレッドシートはURLの最後を `/export?format=pdf` にすることで
pdfとしてエクスポートすることができます。

さらに、パラメータに値を指定することで
実際にスプレッドシートを開いたときのエクスポート設定と同じことができます。

上記コードだと

```
/export?format=pdf&gid=<シートのGID>&range=A1:D14&portrait=false&size=8&fitw=true&vertical_alignment=top&horizontal_alignment=CENTER&scale=3"
```

がその部分に当たります。

例えばpdfとしてエクスポートしたい部分は`range`で指定しています。

指定できるパラメータについては以下のサイトに記載がありますので、
サイズに合わせてカスタマイズしてみてください。

- [margins parameters for spreadsheet export](https://stackoverflow.com/questions/46088042/margins-parameters-for-spreadsheet-export)
- [Example on how to export a Google sheet to various formats, includes most PDF options
](https://gist.github.com/Spencer-Easton/78f9867a691e549c9c70)

カスタマイズしなかった場合は、縦向きのA4サイズで出力されるので
小さい範囲を画像化したいのに余白がかなり大きくなってしまう可能性があり注意が必要です。

## 9. 実行

コードができたらスクリプトを実行し、スプレッドシートのボタンをクリックしてみます。
※画像にはぼかし加工を入れてあります。
※Get_Image_Triggerはwebhookの名前です。

```bash
$ python3 post.py
```

<img width="300" alt="スクリーンショット 2019-08-13 13.29.58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257872/39acd531-6e40-dad7-2a78-28d30f240c81.png">

# 最後に

チャンネル内で利用しているBotは、特定の呪文でスプレッドシートから
情報を引っ張ってきてくれるような機能を他にも実装しており、
スプレッドシート上の情報をチャンネル内に共有したい時などで活躍しています。

本記事では省略した部分が多くあるので、
細かい部分についてはdiscord.pyのドキュメントやGoogleのHelpを呼んでみるといいと思います。