Question about carousel template send via reply_message method.
thkaw opened this issue · 10 comments
When I reply with carousel template, I found in V3 api won't work, and give folloing error code:
linebot.v3.messaging.exceptions.ApiException: (400)
Reason: Bad Request
HTTP response headers: HTTPHeaderDict({'Server': 'legy', 'Content-Type': 'application/json', 'x-content-type-options': 'nosniff', 'x-frame-options': 'DENY', 'x-line-request-id': '7842f750-9181-4c54-8b63-c4a9345738ac', 'x-xss-protection': '1; mode=block', 'Content-Length': '186', 'Expires': 'Wed, 05 Jun 2024 07:02:49 GMT', 'Cache-Control': 'max-age=0, no-cache, no-store', 'Pragma': 'no-cache', 'Date': 'Wed, 05 Jun 2024 07:02:49 GMT', 'Connection': 'close'})
HTTP response body: {"message":"The request body has 2 error(s)","details":[{"message":"May not be empty","property":"messages[0].altText"},{"message":"May not be empty","property":"messages[0].template"}]}
To make sure I really set altText & template attribute, I event write the dict sperately.
NG code
def reply_carousel(reply_token, carousel): #carousel is a TemplateSendMessage object
carousel_dict = {
"type": "template",
"altText": carousel.alt_text,
"template": {
"type": "carousel",
"columns": [
{
"text": column.text,
"actions": [
{
"type": action.type,
"label": action.label,
"text": action.text
} for action in column.actions
]
} for column in carousel.template.columns
]
}
}
messaging_api.reply_message(
ReplyMessageRequest(
reply_token=reply_token,
messages=[carousel_dict]
)
)
So, I tried rollback to old api method, that will be fine.
def reply_carousel(reply_token, carousel):
line_bot_api.reply_message(reply_token, carousel)
Is any limitation between new SDK API and carousel template? Thanks in advance!
Thank you for using line-bot-sdk-python, @thkaw !
To better understand the issue, could you provide a minimal yet sufficient and reproducible code example?
For instance, this example should work:
line-bot-sdk-python/examples/flask-kitchensink/app.py
Lines 324 to 351 in bdda65f
Hi @Yang-33 ,
Thanks for quick replied.
The example you mention is OK, also I use that as my workaround sofar.
However, it will gen following waring log if line_bot_api.reply_message have been invoked
C:\Users\ar801\PycharmProjects\egolden\playground.py:44: LineBotSdkDeprecatedIn30: Call to deprecated method reply_message. (Use 'from linebot.v3.messaging import MessagingApi' and 'MessagingApi(...).reply_message(...)' instead. See https://github.com/line/line-bot-sdk-python/blob/master/README.rst for more details.) -- Deprecated since version 3.0.0.
line_bot_api.reply_message(reply_token, carousel)
Here I put the sample for my question, you should able to reproduce problem by using this code snippet
from flask import Flask, request, abort
from linebot.v3 import WebhookHandler
from linebot.v3.exceptions import InvalidSignatureError
from linebot.v3.messaging import Configuration, ApiClient, MessagingApi, ReplyMessageRequest
from linebot.v3.webhooks import MessageEvent, TextMessageContent
from linebot import LineBotApi
from linebot.models import TemplateSendMessage, CarouselTemplate, CarouselColumn, MessageAction
app = Flask(__name__)
ATOKEN = "PUT YOUR ACCESS TOKEN"
configuration = Configuration(
access_token=ATOKEN)
handler = WebhookHandler('PUT YOUR WEBHOOK CREDENTIAL HERE')
# 初始化 MessagingApi
api_client = ApiClient(configuration=configuration)
messaging_api = MessagingApi(api_client)
line_bot_api = LineBotApi(ATOKEN)
def get_carousel_template_first_use():
return TemplateSendMessage(
alt_text='this is a carousel template',
template=CarouselTemplate(
columns=[
CarouselColumn(
text='QUESTION',
actions=[
MessageAction(
label='ANS A',
text='ANS A'
),
MessageAction(
label='ANS B',
text='ANS B'
)
]
)
]
)
)
@app.route("/callback", methods=['POST'])
def callback():
signature = request.headers['X-Line-Signature']
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
try:
handler.handle(body, signature)
except InvalidSignatureError:
app.logger.info("Invalid signature. Please check your channel access token/channel secret.")
abort(400)
return 'OK'
@handler.add(MessageEvent, message=TextMessageContent)
def handle_message(event):
user_id = event.source.user_id
message_text = event.message.text
reply_carousel(event.reply_token, get_carousel_template_first_use())
# OK case
# def reply_carousel(reply_token, carousel):
# line_bot_api.reply_message(reply_token, carousel)
# NG case
def reply_carousel(reply_token, carousel): #carousel is a TemplateSendMessage object
carousel_dict = {
"type": "template",
"altText": carousel.alt_text,
"template": {
"type": "carousel",
"columns": [
{
"text": column.text,
"actions": [
{
"type": action.type,
"label": action.label,
"text": action.text
} for action in column.actions
]
} for column in carousel.template.columns
]
}
}
messaging_api.reply_message(
ReplyMessageRequest(
reply_token=reply_token,
messages=[carousel_dict]
)
)
if __name__ == "__main__":
app.run(host="0.0.0.0")
thanks, ok let me check the example. (it takes time)
Note the example I provided uses v3 modules instead of deprecated modules.
(client)
line-bot-sdk-python/examples/flask-kitchensink/app.py
Lines 162 to 163 in bdda65f
(usage)
line-bot-sdk-python/examples/flask-kitchensink/app.py
Lines 324 to 351 in bdda65f
I think there are two solutions. The v3 model does not assume passing a dictionary to a method. However, each class in the v3 model has a #from_dict, so you can call that.
For example,
- messages=[carousel_dict]
+ from linebot.v3.messaging import TemplateMessage
+ messages=[TemplateMessage.from_dict(carousel_dict)]Another solution is to stop using dictionaries.
I'm currently working on a project using the LINE bot SDK for Python, and I've encountered an issue with the ReplyMessageRequest when sending messages using reply_message.
In the process of converting to ReplyMessageRequest, the messages field can contain multiple different types (e.g., TextMessageContent, FlexMessage). While using .from_dict works for sending the messages, the format of the dict varies significantly.
Additionally, when running the example code from flask-kitchensink, I encounter the following error:
pydantic.v1.error_wrappers.ValidationError: 1 validation error for ReplyMessageRequest
messages -> 0
value is not a valid dict (type=type_error.dict)Is there a more general way to handle the different message types in ReplyMessageRequest?
Could you specify in which cases the sample fails? I would like a minimal reproducible code that can demonstrate the issue.
As I checked this now, no errors occurred in flask-kitchensink as you said. The error message indicates that the data you provided is not a dictionary.
Sorry, I discovered that my issue was caused by simultaneously importing
from linebot.v3.messaging import (
TextMessage
)and
from linebot.models import *This led to the error. It has now been resolved. Thank you.
I think there are two solutions. The v3 model does not assume passing a dictionary to a method. However, each class in the v3 model has a
#from_dict, so you can call that. For example,- messages=[carousel_dict] + from linebot.v3.messaging import TemplateMessage + messages=[TemplateMessage.from_dict(carousel_dict)]Another solution is to stop using dictionaries.
Thank you provide the information.
However, if I try to change carousel into TemplateSendMessage type (linebot.models.template.TemplateSendMessage)
def reply_carousel(reply_token, carousel):
try:
# OK
# line_bot_api.reply_message(reply_token, carousel)
messaging_api.reply_message(
ReplyMessageRequest(
reply_token=reply_token,
messages=carousel #NG here
)
)
Error shows:
pydantic.v1.error_wrappers.ValidationError: 1 validation error for ReplyMessageRequest
messages
value is not a valid list (type=type_error.list)
Is any possible to pass TemplateSendMessage type to reply_message function?
We cannot use the classes from the unversioned and v3 packages simultaneously. At least, the messaging_api.reply_message from the v3 package does not accept classes from the old SDK. This is because the old classes will eventually be removed from the SDK.
Therefore, please separate the code that creates the template message.
Alternatively, if you have the initial carousel_dict, you can perform the conversion. Please see #641 (comment) for more details.
Understand, thank you for the help :)