tentekoten
@tentekoten

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

LINE Messaging APIでリッチメニューの画像切り替えをする際に404エラーが発生

解決したいこと

LINE Messagin APIを使ってデフォルトのリッチメニューの選択肢で出品者か購入者を選択した際に、専用のリッチメニューを表示させたいです。そこでリッチメニューにアップロードする画像が404エラーが起きてアップロードできない状態になっているので解決方法を教えてください。

発生している問題・エラー

2024-09-26T03:57:17.456010+00:00 app[web.1]: 画像アップロードステータス(出品者用): 404
2024-09-26T03:57:17.456023+00:00 app[web.1]: 購入者用リッチメニューを作成中...
2024-09-26T03:57:17.456024+00:00 app[web.1]: リッチメニュー作成中...
2024-09-26T03:57:17.875782+00:00 app[web.1]: リッチメニューID: richmenu-....
2024-09-26T03:57:17.875960+00:00 app[web.1]: 購入者用リッチメニューID: richmenu-....
2024-09-26T03:57:17.875963+00:00 app[web.1]: リッチメニューに画像をアップロード中...: https://res.cloudinary.com/.../image/upload/.../buyer_image.jpg
2024-09-26T03:57:18.565488+00:00 app[web.1]: 画像アップロードステータス(購入者用): 404

該当するソースコード

import os
from flask import Flask, request, abort
import requests
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage

app = Flask(__name__)

# LINE Messaging APIのチャネルアクセストークンとシークレット
LINE_ACCESS_TOKEN = os.environ["LINE_CHANNEL_ACCESS_TOKEN"]
LINE_CHANNEL_SECRET = os.environ["LINE_CHANNEL_SECRET"]
DATABASE_URL = os.environ["DATABASE_URL"]
HEROKU_APP_NAME = os.environ["HEROKU_APP_NAME"]

line_bot_api = LineBotApi(LINE_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)

# リッチメニューを作成するAPI
def create_rich_menu(rich_menu_data):
    print("リッチメニュー作成中...")  # ログ追加
    url = "https://api.line.me/v2/bot/richmenu"
    headers = {
        "Authorization": f"Bearer {LINE_ACCESS_TOKEN}",
        "Content-Type": "application/json"
    }
    response = requests.post(url, headers=headers, json=rich_menu_data)
    
    # レスポンスからrichMenuIdを取得
    if response.status_code == 200:
        rich_menu_id = response.json().get("richMenuId")
        print(f"リッチメニューID: {rich_menu_id}")  # ログ追加
        return rich_menu_id
    else:
        print(f"エラー: {response.status_code}")  # ログ追加
        print(response.text)
        return None

# リッチメニューに画像をアップロードする関数
def upload_rich_menu_image(rich_menu_id, image_url):
    print(f"リッチメニューに画像をアップロード中...: {image_url}")  # ログ追加
    url = f"https://api.line.me/v2/bot/richmenu/{rich_menu_id}/content"
    headers = {
        "Authorization": f"Bearer {LINE_ACCESS_TOKEN}",
        "Content-Type": "image/jpeg"
    }
    # with open(image_url, 'rb') as img_file:
    #     response = requests.post(url, headers=headers, data=img_file)
    # print(f"画像アップロードステータス: {response.status_code}")  # ログ追加
    # return response.status_code

    # 画像をリモートURLからダウンロード
    response = requests.get(image_url, stream=True)
    if response.status_code == 200:
        response.raw.decode_content = True
        img_data = response.content
        
        # LINEのAPIに画像を送信
        response = requests.post(url, headers=headers, data=img_data)
        return response.status_code
    else:
        print(f"Image download failed: {response.status_code}")
        return response.status_code

# 出品者向けリッチメニューのデータ
rich_menu_data_seller = {
    "size": {"width": 2500, "height": 1686},
    "selected": False,
    "name": "出品者メニュー",
    "chatBarText": "出品者メニューを開く",
    "areas": [
        {
            "bounds": {"x": 0, "y": 0, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "クーポン"}
        },
        {
            "bounds": {"x": 1250, "y": 0, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "お問い合わせ"}
        },
        {
            "bounds": {"x": 0, "y": 843, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "出品する"}
        },
        {
            "bounds": {"x": 1250, "y": 843, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "イベント"}
        }
    ]
}

# 購入者向けリッチメニューのデータ
rich_menu_data_buyer = {
    "size": {"width": 2500, "height": 1686},
    "selected": False,
    "name": "購入者メニュー",
    "chatBarText": "購入者メニューを開く",
    "areas": [
        {
            "bounds": {"x": 0, "y": 0, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "クーポン"}
        },
        {
            "bounds": {"x": 1250, "y": 0, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "お問い合わせ"}
        },
        {
            "bounds": {"x": 0, "y": 843, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "新作アートを見る"}
        },
        {
            "bounds": {"x": 1250, "y": 843, "width": 1250, "height": 843},
            "action": {"type": "message", "text": "購入履歴"}
        }
    ]
}

# 出品者用リッチメニューを作成し、IDを取得
print("出品者用リッチメニューを作成中...")  # ログ追加
rich_menu_id_seller = create_rich_menu(rich_menu_data_seller)
if rich_menu_id_seller:
    print(f"出品者用リッチメニューID: {rich_menu_id_seller}")
    # リッチメニューIDに画像をアップロード
    image_url = "https://res.cloudinary.com/dblgbhtex/image/upload/v1727322302/seller_image_druhei.jpg"
    upload_response = upload_rich_menu_image(rich_menu_id_seller, image_url)
    print(f"画像アップロードステータス(出品者用): {upload_response}")

# 購入者用リッチメニューを作成し、IDを取得
print("購入者用リッチメニューを作成中...")  # ログ追加
rich_menu_id_buyer = create_rich_menu(rich_menu_data_buyer)
if rich_menu_id_buyer:
    print(f"購入者用リッチメニューID: {rich_menu_id_buyer}")
    # リッチメニューIDに画像をアップロード
    image_url = "https://res.cloudinary.com/dblgbhtex/image/upload/v1727322301/buyer_image_yz5bgk.jpg"
    upload_response = upload_rich_menu_image(rich_menu_id_buyer, image_url)
    print(f"画像アップロードステータス(購入者用): {upload_response}")

# ユーザーごとのリッチメニュー割り当て関数
def set_rich_menu_to_user(user_id, rich_menu_id):
    print(f"リッチメニューをユーザー {user_id} に割り当て中...")  # ログ追加
    url = f'https://api.line.me/v2/bot/user/{user_id}/richmenu/{rich_menu_id}'
    headers = {
        'Authorization': f'Bearer {LINE_ACCESS_TOKEN}'
    }
    response = requests.post(url, headers=headers)
    print(f"リッチメニュー割り当てステータス: {response.status_code}")  # ログ追加
    return response.status_code

@app.route("/callback", methods=['POST'])
def callback():
    print("Webhookを受信しました。")  # ログ追加
    # LINEの署名を検証
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    print(f"リクエストボディ: {body}")  # ログ追加
    
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("署名の検証に失敗しました。")  # ログ追加
        abort(400)

    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    print(f"メッセージを受信しました: {event.message.text}")  # ログ追加
    user_id = event.source.user_id
    text = event.message.text

    if text == "購入者として友達になる":
        # 購入者向けリッチメニューを表示
        print("購入者用リッチメニューを設定します。")  # ログ追加
        set_rich_menu_to_user(user_id, rich_menu_id_buyer)
        line_bot_api.reply_message(event.reply_token, TextMessage(text="購入者向けメニューに切り替えました。"))

    elif text == "出品者として友達になる":
        # 出品者向けリッチメニューを表示
        print("出品者用リッチメニューを設定します。")  # ログ追加
        set_rich_menu_to_user(user_id, rich_menu_id_seller)
        line_bot_api.reply_message(event.reply_token, TextMessage(text="出品者向けメニューに切り替えました。"))

if __name__ == "__main__":
    print("アプリケーションが起動しました。")  # ログ追加
    port = int(os.environ.get("PORT", 8000))
    app.run(host="0.0.0.0", port=port)

自分で試したこと

まず最初に同じ層のフォルダにstatic/imagesを作成し、そこに画像を格納し、パスで指定しましたがNot found errorが発生しました。chom 644に設定し他のプロセスでもアクセスできる権限に変更してもエラーは解消されませんでした。

次にcloudinaryを使用して外部のurlからダウンロードする方法で試してみましたが、依然404エラーが発生するままです。

自分で調べてみても解決方法が記載されている記事が見つからないため、お教えていただけますと嬉しいです。
よろしくお願いいたします。

0

1Answer

404はあなたの持っている画像の問題ではなく、存在しないLINE APIにアクセスしているというエラーです。

curl -v -X POST https://api-data.line.me/v2/bot/richmenu/{richMenuId}/content \
-H "Authorization: Bearer {channel access token}" \
-H "Content-Type: image/png" \
-T richmenu-template-guide-04.png

この通り、画像のアプロード先は、
api.line.me ではなく api-data.line.me ではないですか?

0Like

Your answer might help someone💌