Linebot へ画像を送信する
🚀 1. LINE Messaging API の画像送信の条件
LINE の push メッセージや reply メッセージで画像を送信する際、
originalContentUrl と previewImageUrl には 「インターネット上でアクセスできる URL」 を指定する必要があります。
✅ 使用できる URL の条件
1. インターネット上に公開されている
2. https でアクセスできる
3. 画像サイズが 1MB 以下(それ以上は送信できない)
4. MIMEタイプが image/jpeg, image/png, image/gif のいずれか
5. CORS制限がない(Google Drive の「共有リンクを取得」で”リンクを知っている全員が閲覧可能”にする)
ということで
Google drive へ保存し公開にすることで送信できるようにする
既に tmpフォルダを作成済みで
ここへ画像を保存することにする
Google drive API を使うためservice_account.jsonの取得が必要
なお権限の関係上、新しいプロジェクトを作成し
サービスアカウントの作成する
まずプロジェクト名を設定
次にこのプロジェクトに切り替え
Google Drive API を有効化
サービスアカウントを作成
1. 「APIとサービス」 → 「認証情報」 を開く
2. 「+ 認証情報を作成」 → 「サービスアカウント」 を選択
3. サービスアカウントの名前を入力(例: linebot-drive-access)
4. 「作成」ボタンをクリック
5. 「ロールの選択」で編集者を選択
ユーザーにこのサービスアカウントへのアクセスを許可(省略可)
は省力してOK
次に
Google Drive の「共有設定」にサービスアカウントのメールを追加
サービスアカウントのメルアドになるので
これを追加する
次に
サービスアカウントの JSON キー(service_account.json)を取得
APIとサービス」 → 「認証情報」 に戻る
作成したサービスアカウントをクリック
「キー」タブを開く
「鍵を追加」→「新しい鍵を作成」
「JSON」 を選択し、「作成」をクリック
自動的に service_account.json がダウンロードされる
cp ~/Downloads/linebot-gdrive-fd48d69bd326.json .
でコピー
mv linebot-gdrive-fd48d69bd326.json service_account.json
でファイル名を変える
次に動作確認
vim drive_access.py
内容は
from googleapiclient.discovery import build
from google.oauth2 import service_account
# ① サービスアカウントの JSON キーファイル
SERVICE_ACCOUNT_FILE = "service_account.json"
SCOPES = ["https://www.googleapis.com/auth/drive"]
# ② 認証情報をセット
creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
drive_service = build("drive", "v3", credentials=creds)
# ③ Google Drive の temp フォルダの ID
FOLDER_ID = ""
def list_files():
query = f"'{FOLDER_ID}' in parents and trashed=false"
results = drive_service.files().list(q=query, fields="files(id, name)").execute()
return results.get("files", [])
# ④ 画像一覧を取得
files = list_files()
if files:
for file in files:
print(f"File: {file['name']}, ID: {file['id']}")
else:
print("No files found in the Drive folder.")
これで
python drive_access.py
を実行すると
File: PXL_20240617_182349485.jpg, ID:
というようにアクセスできるのがわかる
次に画像の送信のテストをする
その前に公開ディレクトリの確認をする
vim open_folder_check.py
中身は
from googleapiclient.discovery import build
from google.oauth2 import service_account
# 認証情報のセットアップ(Google Drive API 用のサービスアカウント)
SCOPES = ["https://www.googleapis.com/auth/drive.metadata.readonly"]
SERVICE_ACCOUNT_FILE = "path/to/service_account.json"
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES
)
service = build("drive", "v3", credentials=creds)
# 共有されているフォルダを検索
query = "mimeType='application/vnd.google-apps.folder' and sharedWithMe"
results = service.files().list(q=query, fields="files(id, name, owners, webViewLink)").execute()
folders = results.get("files", [])
if not folders:
print("公開されているフォルダはありません。")
else:
for folder in folders:
print(f"フォルダ名: {folder['name']}, URL: {folder['webViewLink']}, 所有者: {folder['owners'][0]['emailAddress']}")
実行ログから公開されているのが確認できた
GASコードを
const LINEAPI_TOKEN = PropertiesService.getScriptProperties().getProperty('LINEAPI_TOKEN');
const PUSH_URL = 'https://api.line.me/v2/bot/message/push';
/**
* Webhook (Pythonからリクエストを受け取る)
* @param {Object} e - HTTP POST リクエスト
* @returns {ContentService} - 成功・失敗のレスポンス
*/
function doPost(e) {
try {
const requestData = JSON.parse(e.postData.contents);
const userId = requestData.userId; // 送信先のLINEユーザーID
const imageUrl = requestData.imageUrl; // 送信する画像URL
const message = requestData.message; // 画像と一緒に送るテキスト
if (!userId || !imageUrl) {
throw new Error("userId または imageUrl がありません");
}
sendImageToLine(userId, imageUrl, message);
return ContentService.createTextOutput(JSON.stringify({ status: "success" })).setMimeType(ContentService.MimeType.JSON);
} catch (error) {
console.error(error);
return ContentService.createTextOutput(JSON.stringify({ status: "error", message: error.message })).setMimeType(ContentService.MimeType.JSON);
}
}
/**
* LINE に画像メッセージを送信
* @param {string} userId - 送信先の LINE ユーザーID
* @param {string} imageUrl - 送信する画像URL
* @param {string} message - 画像と一緒に送るテキスト
*/
function sendImageToLine(userId, imageUrl, message) {
const payload = {
"to": userId,
"messages": [
{
"type": "text",
"text": message
},
{
"type": "image",
"originalContentUrl": imageUrl,
"previewImageUrl": imageUrl
}
]
};
const options = {
"method": "post",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer " + LINEAPI_TOKEN
},
"payload": JSON.stringify(payload)
};
const response = UrlFetchApp.fetch(PUSH_URL, options);
console.log("LINE送信結果: " + response.getContentText());
}
としてデプロイ
import requests
import random
import time
from googleapiclient.discovery import build
from google.oauth2 import service_account
# ① Google Drive API の設定
SCOPES = ["https://www.googleapis.com/auth/drive"]
SERVICE_ACCOUNT_FILE = "service_account.json"
creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
drive_service = build("drive", "v3", credentials=creds)
# ② 送信先の LINE API 設定
LINE_PUSH_URL = "https://api.line.me/v2/bot/message/push"
LINE_CHANNEL_ACCESS_TOKEN = ""
USER_ID = ""
# ③ Google Drive の「temp」フォルダID
FOLDER_ID = ""
def get_drive_images():
"""
Google Drive の temp フォルダ内の画像リストを取得し、画像の `id` をリストで返す
"""
query = f"'{FOLDER_ID}' in parents and mimeType contains 'image/' and trashed=false"
results = drive_service.files().list(q=query, fields="files(id, name)").execute()
files = results.get("files", [])
if not files:
print("No images found in the Drive folder.")
return None
return [file["id"] for file in files]
def get_drive_image_url(file_id):
"""
Google Drive のファイルIDを画像URLに変換する
"""
return f"https://drive.google.com/uc?id={file_id}"
def send_image(image_url):
"""
LINE API を使い、取得した画像URLを LINE ユーザーに送信する
"""
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {LINE_CHANNEL_ACCESS_TOKEN}"
}
payload = {
"to": USER_ID,
"messages": [
{
"type": "image",
"originalContentUrl": image_url, # オリジナル画像URL
"previewImageUrl": image_url # プレビュー用画像URL
}
]
}
response = requests.post(LINE_PUSH_URL, headers=headers, json=payload)
print(response.status_code, response.text)
def main():
"""
画像リストを取得し、ランダムな画像を選んでLINEに送信
"""
image_ids = get_drive_images()
if not image_ids:
print("No images found, skipping LINE push.")
return
# 画像をランダムに選択
random_image_id = random.choice(image_ids)
image_url = get_drive_image_url(random_image_id)
# LINE に画像を送信
send_image(image_url)
# スケジュール実行(30分ごと)
if __name__ == "__main__":
while True:
main()
time.sleep(1800) # 30分ごとに実行
で実行したが
画像が白いので共有フォルダにアクセスできていないらしい