This CM has a strong impact. I love this so when I invite him to a class meeting "** I'm in Singapore now **" I have created a ** LineBot ** that will reply to you. In addition, various specifications have been added to further enhance the ** doya feeling **. Even if it doesn't remain on the map, I want to make it a ** memorable job ** for the person who used it!
This is the 4th day of Fucking App Advent Calendar 2019. ~~, a feeling of a fucking app that drifts from the title without writing ~~
① ** Hold a class meeting ** ② ** Start Line and ask "Ayano, where are you now?" ** ③ ** "I'm sorry, I can't attend the class meeting ~~~ I'll omit the following" ** ④ ** Everyone looks into it with the feeling that "Eh, Singapore" **
For those who haven't seen it yet, please see the CM itself below.
[](https://www.youtube.com/watch?v= HQYQ3Me2KLw) https://www.youtube.com/watch?v=HQYQ3Me2KLw
It implements various ~~ extra ~~ functions. The first is "** Jobs that remain on the map **".
**Singapore! !! To make ** more appealing I kindly tried to show a map showing the location of Singapore. With this, ** the feeling that remains on the map ** is perfect ☆
In addition, the international AYANO ** Fly around over 200 countries and regions (awesome) ** If you ask a question other than a fixed phrase, She is not always in Singapore. You will hear replies from various places!
Because you can find the location even with an unfamiliar country name ** It's also an app for studying world geography! ** ** ~~ It may not have been a fucking app. ~~
Oops, if you look closely, in the screen capture above It seems that he is inviting him to a "class reunion". ** Immediately respond to the content of the invited party **!
Please invite anything, whether it's "** Gokon " or " Sakura wo Miru Kai **".
AYANO also makes things other than the subway.
We are making various things besides Laputa! Please check for yourself the various other creations. ~~ Put someone's youth on = Theta & Pazu. Put you on. ~~
Although we will respond according to the content of the invitation such as the second party, With the functions so far, there is a "random message display feeling", Maybe to a friend who looked into me together "** Oh, isn't this AYANO a bot? **" It may be bald.
So, in response to the keywords of the conversation you threw, Something ** added a function to return meaningful words **. (Red frame) ~~ Highly conscious conversation. It seems that ramen is nostalgic. ~~
Of course AYANO can understand Japanese, so ** You can recognize the "important words" that appear in the conversation. ** ** This time, I recognized that "noun / general" was important. If it doesn't exist, it will reply "Jobs that remain on the map". In the above example, it seems that it recognizes "ramen". ** This creates reality for AYANO and is memorable! ** **
Morphological analysis itself is no longer a terrific technique. However, the implementation to be built into GAE (like Serverless / AWS Lambda) The point is that it is not very familiar (it is a little difficult to realize). ** There is no external API call, and it is explosive. Super immediate less ☆ **
** Now, at various places such as class meetings and drinking parties ** ** You can now talk with AYANO all over the world ☆ **
** When used with friends, it can be seen with warm white eyes, ** ** There is no doubt that it will be a "memorable job" ☆ **
So that's all for the introduction of the contents Implementation ideas, code, I will write various know-how on creating LineBot below.
First of all, how do you make LineBot in the first place? So, please refer to the following page. (This is an article on the development blog of people in Line.)
I made a bot that can keep up with the last train using image map messages
The base configuration will be created in the same way as this article. ** Python + Flask ** (However, the Python version has changed from 3.6 to 3.7) And the map image uses ** Google Static Maps API ** as well as the article.
First of all, it is a good idea to deploy "Aum Return Bot". However, ** just writing and executing this code does not make it a LineBot **. How should I move it?
Most of the information about making LineBot seems to be deployed on ** Heroku **. The reason for using Heroku is ** because it's easy to use HTTPS **.
To create a LineBot, first log in to LINE Developers. (You can use your own Line account as it is)
There is a place to specify "Webhook URL" after login, Only ** HTTPS can be specified to specify the URL **. Also, when specifying an image file for transmission in the code, etc. The Line Messaging API requires ** HTTPS-URL to be specified **. (You cannot send a local file. Be sure to specify it by URL)
In other words, creating an HTTPS server is almost essential for ** LineBot development **.
There are three main options here. ① ** Set up your own server and make it HTTPS ** ② ** Use serverless / PaaS service ** (Heroku, GAE, Lambda, etc.) ③ Use a dedicated service such as ** Twilio ** and have them take charge of the server part
③ is the most recommended for simple response bots. This time I'm writing a little complicated code myself, so it's either ① or ②. Although ① can be realized for free, server preparation / operation is a little troublesome. Therefore, ② is the best option.
Probably because of the Line official example and the above reasons Information on how to make most LineBots It seems that Heroku is a prerequisite. However, ** essentially anything is fine as long as HTTPS can be easily realized **, so There are no elements that depend on Heroku.
This time I changed my mind a little I would like to use ** GAE (GoogleAppEngine) **. The code for the article for Heroku works pretty well.
** GAE (Standard Environment) has a 28-hour free tier at all times per day. ** ** (As of November 2019) Hey, I think it's 24 hours a day, right? When multiple instances are launched at the same time depending on the load status, If you launch a better instance in terms of performance It is a mechanism that counts multiple minutes of time. In other words, if the load can be processed by one instance with the minimum configuration, Even if there are a few at the peak The image is that it always fits in a free frame.
The range of use that can be used free of charge even after one year. For example, "Qiita Hall of Fame" uses this always-free tier in IaaS.
Run the GAE Python 3.7 Flask tutorial
(Just git clone
and gcloud app deploy
and it's done right away)
If you change main.py
to the reference code, the LineBot is almost complete.
(Also, add line-bot-sdk == 1.14.0
to requirements.txt
)
The best way to use the Line Messaging API in personal development is It seems to be ** designing a service with Reply **.
The LineMessagingAPI has a pay-as-you-go limit for sending Push-type messages. With the free tier, you can only send 1000 messages a month. Even if you charge, if you exceed a certain number, you will be charged for about 3 to 5 yen per copy. (For details, please check the official website / latest information etc.)
However, ** Reply can be used for free **.
As the first design of the big picture of the service ** "Service design that makes it natural to reply to utterances from users" ** It would be desirable to have a form that does not depend on Push-type transmission.
However, Reply can only be used for a certain period of time after the user speaks. Only reply once for one utterance & only 5 balloons at a time, It is also important to note that it can only be used.
For example, if you want to make an "** counting prime number and calming app **", If the bot unilaterally talks to 13, 17, 19 ... every 3 seconds, It will be a Push type. next? next? Ask the user each time So that it is natural to have them answer 23, 29, etc. It is better to think about the design of the entire service.
This time AYANO said, "Let's go to the class meeting!" Just as it is quite natural to reply to an invitation It fits in the service design!
The following locations of the reference source article, For "YOUR_CHANNEL_ACCESS_TOKEN",
line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN')
Free / can be issued as many times as you like on LINE Developers Set the "Channel Access Token (Long Term)". It's at the bottom of "Messaging API Settings".
But this access token is It is necessary to set the expiration date at the time of issuance, "Time until the current channel access token becomes invalid" ** You can only set up to 24 hours with the pull-down **.
Normally, set it to 24 hours, and at a pace of about every 23 hours. I need to create an extension / rewrite process, Here is the ** real trick ** (at the level of wondering if it's okay to write).
If you set ** time = 0, it will be a permanently valid token **. (As of November 2019)
** Awesomely fast ~~ Knifehand strike ~~ If you don't have me, you'll miss it ** With that in mind, let's thank you for using it. (This line is a complete death flag)
It's not a Line bug, but the access token expiration date is It seems that it was indefinite at the beginning. Changing to put a time limit, A transitional period of that change? Many oppositions take time to change? Seems like For that, basically set the effective time, It seems that the situation is that people who know it can use it infinitely. (Including rumor & evil guess. Truth unknown)
I don't know the best way to automate the expiration of this token, (I want to make serverless basically stateless) Should I stop developing LineBot just because there is a deadline for this guy? Because it was a critical point that I thought ** I sincerely hope that you will continue to use it indefinitely **.
This is usually from LINE Developers It's a story that is half buggy. (As of November 2019)
As mentioned above, in the "Webhook URL" of LINE Developes, The GAE URL will be listed as the HTTPS URL. Is not OK even if I press the "Confirm connection" button.
So, in the handle definition of the original code, as follows Add a processing branch with a special reply_token.
#The following is the handle definition
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
#Special code to make "connection confirmation" of Line OK
#When you press "Check Connection" on Line's Developer Console
#If you make it normally, reply_500 returns due to illegal token.
#I get angry if the expected 200 is not returned on the connection confirmation side.
#Problems on Line's console as of November 2019. Probably will be resolved eventually.
if event.reply_token == "00000000000000000000000000000000":
return
#Omitted below
It does not mean that it will not work unless the "connection confirmation" is passed. However, it's still a matter of mood, so I want to make connection confirmation = OK. For more information, see the great pioneer Qiita article below.
About the error when checking the connection of the LINE Developes Webhook URL
The most confusing process in this development is The process of displaying a map, that is, It is a process to send the result of ** Google Static Maps API ** to Line.
First, what is the Google Static Maps API? With the map API provided by Google A thing that made Google MAP a static image It can be obtained by specifying simple parameters.
What is amazing is not only the designation by latitude and longitude, but also Singapore, Yokohama, etc. It also supports returning coordinates by specifying it with a place name. A list of countries and regions around the world where AYANO is flying It has information in a simple LIST type. Even if you don't have GPS coordinates in that LIST Just throw the country name into the Google Static Maps API, You are telling us the coordinates. Easy. Please google for other detailed options as appropriate.
So ** How do I send that Google Static Maps API image to Line? ** **
LineMessagingAPI has an API for sending images, Please specify the image for sending with the URL of https, Is written in the reference.
And the Google Static Maps API Access the static map image over HTTPS.
** Google Static Maps API URL ** ** You can specify it as an argument of LineMessagingAPI! ** ** ** ⇒ Lost. ** **
The URL for the Google Static Maps API is HTTPS. But the image file doesn't "exist" directly in that path, Because it is calculated and displayed in Google ** Seen from the Line side, it's not an image, right? It seems that it looks like ** ** This method doesn't seem to work. ** ** (* The exact reason why it doesn't work can only be understood by asking the person inside. Maybe because the API key is also included in the URL parameter I thought it would be impossible because of that certification.)
Therefore, only the images created by Google Static Maps API It seems to be displayed in a pseudo manner on my own server (this time on GAE) You need to have the code ready. This is the part of the code part described later.
@app.route("/imagemap/<path:url>/<size>")
def imagemap(url, size):
#The following is omitted
The same is done in the base article of the reference source. At first glance, I didn't quite understand the intent of the code, so I added a commentary.
Furthermore, as a result of adjusting the processing so that any designated Japanese point can be displayed, If you add excerpts and comments only to the key points, It will be as follows.
#■ Google Map related settings
#Enter the key for Google's Static Maps API.
google_staticmaps_api_key="YOUR GOOGLE STATICMAPS API KEY"
#Specify the size to use when sending with Google (* Maximum 640 on Googles side*Up to 640)
IMAGE_SIZE = 640
#markers=Enter the country name and automatically search and display
#https://maps.googleapis.com/maps/api/staticmap?markers=color:blue|%22%E3%82%B7%E3%83%B3%E3%82%AC%E3%83%9D%E3%83%BC%E3%83%AB%22&size=300x300&zoom=3&language=jp&key=YOUR GOOGLE STATICMAPS API KEY
#input_Since basyo is entered in Japanese, it will be returned after URL encoding.
def makeMapUlr(input_basyo):
map_image_url = 'https://maps.googleapis.com/maps/api/staticmap?markers=color:{}|{}¢er={}&zoom=3&language=jp&size={}x{}&key={}'.format('blue', urllib.parse.quote(input_basyo), urllib.parse.quote(input_basyo),IMAGE_SIZE, IMAGE_SIZE, google_staticmaps_api_key)
return map_image_url
#Function to process into a message for displaying a map of Line
def makeImagemapSendMessage(map_image_url):
#Flask host name will be entered:
request_host_name = request.host
print(request_host_name)
base_url = 'https://{}/imagemap/{}'.format(request_host_name, urllib.parse.quote_plus(map_image_url))
print(base_url)
message = ImagemapSendMessage(
base_url = base_url,
alt_text = 'world map',
base_size = BaseSize(height=IMAGE_SIZE, width=IMAGE_SIZE),
)
return message
#Performs the process of returning the image acquired from Google based on the received request.
@app.route("/imagemap/<path:url>/<size>")
def imagemap(url, size):
print("imagemap-get-called")
#For debugging
print(url)
print(size)
map_image_url = urllib.parse.unquote(url)
response = requests.get(map_image_url)
img = Image.open(BytesIO(response.content))
img_resize = img.resize((int(size), int(size)))
byte_io = BytesIO()
img_resize.save(byte_io, 'PNG')
byte_io.seek(0)
return send_file(byte_io, mimetype='image/png')
#Using the code above,
# line_bot_api.reply_message
#Of the argument of, reply_Just make a message equivalent and pass it
#(The return value of makeImagemapSendMessage is that)
#
# line_bot_api.reply_message(
# event.reply_token,
# reply_messages
# )
** Specify the image with the HTTPS URL, ** ** But you can't just enter the URL of Google Static Maps. ** ** I think this is ** super addictive **.
By the way, this time AYANO ** As I was waiting for a classmate invitation ** ** And prepare a map of Singapore ** ** I will reply to you with a super-immediate reply. ** ** To that extent, I was invited to the class meeting ** Don't bite too much **, Isn't it possible to return it naturally every 10 seconds?
In conclusion, I couldn't. (I didn't understand)
What if I reply more than once with wait? ⇒ It seems that reply_token can only be used once.
Is it possible to specify the interval and wait in the LineMessagingAPI? ⇒ Reference does not seem to be there
Furthermore, if you wait GAE: I feel that it is structurally incompatible with serverless. (I don't think the billing amount will increase significantly. It's a matter of mood)
As a last resort, if you reply first, then wait and then push, It's not impossible. As mentioned above, Push is expensive, so I rejected it.
This time, ** AYANO who was waiting for the invitation of the classmate ** Please forgive me with the setting. ** Appeal Singapore with super-immediate less **.
Natural language processing x Python is Mecab, Janome, Also, I often used libraries such as Gensim in Word2Vec.
By making a chatbot, I would like to use these libraries / functions as well.
However, a big wall stands here. ** GAE can only use libraries that are lightweight to install (explanatory miscellaneous) **
Although Mecab gave up from the beginning, Janome and Gensim also Even if you adjust the memory size of the instance to the maximum It could not be used on GAE due to memory over. With a little ingenuity, it's unlikely that it will be free.
** Therefore, as a morphological analysis tool that can be used even with the smallest instance of GAE ** ** I tried using "igo-python". ** ** It's lightweight, it moves quickly, and it's wonderful.
The usage is the same as Mecab and Janome, as shown in the code below. However, this time the dictionary etc. have not been expanded, It should be noted that the accuracy of morphological analysis is quite low.
igo-How to use python
from igo.Tagger import Tagger
#Morphological analysis: igo-Initialization of python
t = Tagger()
#Function that returns the morphological analysis result as a character string
def extract_str(input_str):
parsed_list = t.parse(input_str)
result_str = ""
for m in parsed_list:
result_str += m.surface + " / " + m.feature + " \n"
return result_str
#noun-A function that extracts only the general and returns it as a list
def meisi_tyuusyutu(input_str):
result_list = []
parsed_list = t.parse(input_str)
for m in parsed_list:
feature_list = m.feature.split(',')
#noun-General, particles-Attributive form, particle-It becomes like a particle.
hinsi_info = feature_list[0] + "-" + feature_list[1]
if hinsi_info == "noun-General":
result_list.append(m.surface)
return result_list
Setting method when deploying to GAE, How to write "requirements.txt" is as follows.
Flask==1.1.1
line-bot-sdk==1.14.0
igo-python==1.0.0
So ** We were able to achieve morphological analysis with the minimum configuration of GAE! ** **
This time, I decided to use igo-python without considering the accuracy. If you want natural language processing & accuracy within LineBot Proposal (1) Set up your own HTTPS server and install it yourself Proposal (2) Use COTOHA if you want to do it on GAE or Heroku. Etc. seems to be the correct way to proceed.
Also, for the implementation that runs Word2Vec / Gensim equivalent without a server, I'm in the middle of the road so I'll see you off. Someday on another occasion. (Up to the point of moving simple things, it is realized in a surprising way. Since the model file itself is heavy, the balance with accuracy is ... )
Now, like the above-mentioned experiment of incorporating natural language processing, If processing fails due to GAE memory dependence
** It works during local development, but ** ** Does not work when uploaded to the server **
There are many cases.
Also, LineBot requires an HTTPS server, which also It is difficult to test in a local development environment, so In the vicinity of the lineMessagingAPI-GoogleStaticMapsAPI linkage, ** Processing that can be confirmed only after deploying to GAE ** occurred frequently.
In these cases, if you send a chat to AYANO, ** Ignore read **, will be repeated. If you read and ignore it every time you deploy, it will be ** heartbreaking **.
I haven't found a very good solution for this.
For the time being, as a slightly better method, (1) The standard output log such as print is Since you can see them collectively with "Logging (Stackdriver)" in GCP, Write a print in a suitable place when debugging (2) To be able to separate Line-related problems from other problems From GET access to the server without going through Line Make it so that you can hit the created function Is it two points?
Below are the main processes of Flask-LineBot. Comments are added with ★ in the places corresponding to ①②.
python
#Line's WebHook is POST type and is not used for normal GET URL access
#The following is for checking the communication of the server
#★ In this way, create a place where you can check other than Line's WebHook,
#If you call the function you want to check in these places,
#It is possible to isolate problems with Line related problems.
@app.route("/", methods=['GET'])
def sayhello_root():
"""Return a friendly HTTP greeting."""
return '[200] It works!'
#Set to Line's WebHook and receive + reply to messages
#If you want to send a message with a bot, on Line's Developer console,
#A message will be sent by POST to the URL described in "Webhook URL * SSL only supported".
#Reference: https://www.casleyconsulting.co.jp/blog/engineer/3028/
@app.route("/", methods=['POST'])
def callback():
#Get the value for signature verification from the request header
signature = request.headers['X-Line-Signature']
#Get request body
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
#Verify the signature, and if there is no problem, call the function defined in handle.
try:
handler.handle(body, signature)
#If signature verification fails, raise an exception.
except InvalidSignatureError:
abort(400)
return 'OK'
#The following is the handle definition
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
#Special code to make "connection confirmation" of Line OK
#When you press "Check Connection" on Line's Developer Console
#If you make it normally, reply_500 returns due to illegal token.
#I get angry if the expected 200 is not returned on the connection confirmation side.
#Problems on Line's console as of November 2019. Probably will be resolved eventually.
#Reference: https://qiita.com/q_masa/items/c9db3e8396fb62cc64ed
if event.reply_token == "00000000000000000000000000000000":
return
#Get the message in the event
input_text = event.message.text
#★ Standard output is aggregated and displayed in "Logging" in GCP, so
#It is easy to debug if you print out at key points.
print(input_text)
#Parse the message and create "line messages" for reply
#★ Without writing all the processing in the webhook or handler
#The main process is created by an external function,
#Without going through a webhook or handler
#Make it so that you can call and confirm directly
reply_messages = makeLineMessages(input_text)
#reply_message is free in principle,
#reply_The token has a valid period and cannot be processed for a very long time.
#The following is the basic form:
#line_bot_api.reply_message(
# event.reply_token,
# [
# TextSendMessage(text= reply_text),
# ]
#)
#Reply and finish.
line_bot_api.reply_message(
event.reply_token,
reply_messages
)
#Flask on GAE (python3).Sample to move in 7):
#「gae_python37_Created based on "app"
if __name__ == '__main__':
# This is used when running locally only. When deploying to Google App
# Engine, a webserver process such as Gunicorn will serve the app. This
# can be configured by adding an `entrypoint` to app.yaml.
app.run(host='127.0.0.1', port=8080, debug=True)
This concludes the technical topic.
In the past days, I had the opportunity to visit Line because of a certain activity (outside business). It was a very enjoyable time for us to respond / introduce us very kindly. We would like to take this opportunity to thank you again.
At that time, he also answered in detail the technical QA about LineBot. This article is a comprehensive article of the knowledge / know-how gained from the QA. (Because it was said that you can write it on Qiita etc. ~~ (rather recommended to write) ~~) ** LineBot development for the first time! At that time, I intend to summarize the points that I am very worried about **.
But the result of QA is "I'm sorry, I can't go to the classmate bot" You wouldn't have thought at all! ~~ I'm really sorry. ~~
I touched both LineBot and GAE for the first time.
When creating a LineBot, explain "Why you have to reply with Line (bot)" How do you design it? I feel that is the biggest point. LIFF (LINE Front-end Framework) Although there is a function to embed a web application in Line, It is not possible to realize the application with Line, but it is essential to realize it within Line. It should be designed. The simplest way to achieve that design is with a "** character **". This time, in order to increase the presence of AYANO, it was essential to realize it on Line.
There are many pages that explain how to make a LineBot, but this one ** Why should I make it as a LineBot? How to design a bot? ** is I have the impression that it is not often talked about.
For example, another possibility is "** Group chat or dialogue with friends **". If you want to make an "Othello (Reversi) app", With web apps and smartphone apps, it seems a little difficult to implement battles between friends. The same applies to schedule sharing apps. By implementing it on Line / LIFF, if you call a bot for group chat, It seems that such a problem can be solved. If you want to create a match against AI or a matching match against strangers Create with a web app or smartphone app, and on Line for the purpose of playing against friends, It is better to think about the design first. Another policy is to create a bot for the purpose of supporting human intervention as appropriate.
Considering the ** "characteristics" ** of LineBot, I think it's easy to come up with the next idea! !!
GAE (serverless) handles natural language processing To be honest, it was harder than I expected. Natural language processing originally requires "dictionary" and "word-by-word data", so The problem is that it is "heavy" no matter where it is implemented.
For example, in the article below, take the plunge I have also tried implementing it on the front end (JavaScript / PWA) side. https://qiita.com/youwht/items/6c7712bfc7fd088223a2
The processing became the fastest when it was moved to the front side, Because the morphological analysis library is included in the package The distribution file size at the first startup is a little heavy I felt it as an issue. (The accuracy is not good enough)
Even in GAE (serverless), depending on the weight, Decent accuracy x processing content is not assembled.
GAE itself is when making light processing No infrastructure required, just Python code to become an HTTPS server, It's pretty convenient. However, there is still a habit, I don't feel like I can do anything that Python can do, You will need to be careful when using it.
This time, although natural language processing is heavy, Up to this level, you can do it with GAE (serverless), right? I think it was good to get the feeling!
** "I'm in Singapore now." ** ** If you decline any invitation ** ** Line My only friend is AYANO. ** **
** Actually, I know AYANO is a bot, but ** ** But now I pretend I don't know a little more. ** **
** The fucking app I make is also ** ** I'm sure someday heal someone's loneliness **
That's it from Singapore.