之前看到網路上很多人都在玩LINE 的LIFF,尤其是看到官方文章這篇,感覺就超級有趣的,想說自己也來寫一個玩看看,因為之前比較少接觸JavaScript,所以花了一點時間研究一下,下面來簡單講一下實作過程吧~
* LIFF(Line Front-end Framework)是一個可以在 LINE app 內運作的 web app 平台,並且浮現在對話視窗中。
* 相關LIFF 官方介紹及可以使用的API文件 => Link
* 除了要會一些python寫Chatbot,還要學一下HTML與JavaScrip一下
Target:
目標是建一個可以畫圖並傳送出來的LIFF,同時想說也順便加上可以即時翻譯的LINE BOT, 練習兩樣功能的實作。最後呈現如下圖,我畫的有點醜請見諒XD
Step:
1. 建立draw連結對象
如果要用LIFF畫圖就要先建立一個可以連結畫圖的網站,所以首先要先用簡單的HTML與JavaScript來架一個給我LIFF畫圖的連結(link),下面先放上我簡單寫的程式碼,很少在寫前端,所以有比較奇怪地方請告知一下。
我的後端是使用用Python的Flask框架,而前段就用HTML + Node.js
下面有幾點要注意一下
* 如果是使用Flask框架的話要注意資料夾的階層位置次序,因為Flask在跑的時候會依照資料夾名稱去抓要使用到的檔案,所以資料夾名稱也要正確才行,templates 與 static 都是相對應於主程式.py的位置,templates資料夾是存放副檔名為.html的檔案 ; 而 static 裡面分別有images 資料夾存放圖片檔案與js 資料夾存放副檔名為.js的檔案。
* 每次在push js檔案上去時候要記得在 <script src="..address...xxx.js?v=138"></script> 裡面更改v=後面的參數(亂打即可),如果沒有更改的話,Chrome還是會去抓之前沒修改過的,我試過刪除cookie還是一樣。
* 把所有檔案都Push到Heroku後,測試過網址沒問題後,就要到Line Developers,找到你要使用的機器人 Channel,點選列表的LIFF,就可以加入了喔~
* 圖片如果要存起來需要另外加入資料庫,我這邊是使用Heroku來暫存,所以畫完的圖過沒多久就會在heroku的路徑上消失了
資料檔案階層結構
draw.js
window.onload = function() { var canvas = document.getElementById('main'); var canvastop = canvas.offsetTop var context = canvas.getContext("2d"); var lastx; var lasty; var color = "#000000" var width_can = 1; context.strokeStyle = color; context.lineCap = 'round'; context.lineJoin = 'round'; context.lineWidth = width_can; function colort() { switch (this.id) { case "green": color = "green"; break; case "blue": color = "blue"; break; case "red": color = "red"; break; case "yellow": color = "yellow"; break; case "orange": color = "orange"; break; case "black": color = "black"; break; case "white": color = "white"; break; case "gray": color = "gray"; break; case "lightgreen": color = "#99FF99"; break; case "lightblue": color = "#99FFFF"; break; case "lighgray": color = "#DDDDDD"; break; } } function clear() { context.fillStyle = "#ffffff"; context.rect(0, 0, 350, 330); context.fill(); } function share() { $.ajax({ type: 'POST', url: '/fileupload', data: { 'image': canvas.toDataURL('image/jpeg', 1.0) }, success: function (res, status) { liff.getProfile().then(function (profile) { liff.sendMessages([ { type: 'image', originalContentUrl: 'https://yourURL......herokuapp.com/static/images/' + res + '.jpg', previewImageUrl: 'https://yourURL.....herokuapp.com/static/images/' + res + '_240.jpg' }, { type: 'text', text: '猜猜看這是什麼' } ]).then(function () { liff.closeWindow(); }).catch(function (error) { alert('Error sending message: ' + error.message); }); }).catch(function (error) { alert("Error getting profile: " + error.message); }); }, error: function (res) { alert('Error saving image: ' + res.status); }, complete: function(data) { } }); liff.sendMessages([ { type: 'image', originalContentUrl: imgData, previewImageUrl: imgData, } ]).then(()=> { liff.closeWindow(); });*/ } function showimage(){ switch (this.id) { case "bear": url = "yor pic url" break; } var img = new Image(); img.onload = function(){ context.drawImage(img, 0, 0, 350, 330); }; img.src = url; } function dot(x,y) { context.beginPath(); context.lineWidth = width_can; context.strokeStyle = color; context.fillStyle = "#000000"; context.arc(x,y,1,0,Math.PI*2,true); context.fill(); context.stroke(); context.closePath(); } function line(fromx,fromy, tox,toy) { context.strokeStyle = color; context.lineWidth = width_can; context.beginPath(); context.moveTo(fromx, fromy); context.lineTo(tox, toy); context.stroke(); context.closePath(); } function show(){ num = document.getElementById("num"); num.innerHTML = "數值顯示:" + ran.value; width_can = ran.value; } canvas.ontouchstart = function(event){ event.preventDefault(); lastx = event.touches[0].clientX; lasty = event.touches[0].clientY - canvastop; dot(lastx,lasty); } canvas.ontouchmove = function(event){ event.preventDefault(); var newx = event.touches[0].clientX; var newy = event.touches[0].clientY - canvastop; line(lastx,lasty, newx,newy); lastx = newx; lasty = newy; } ...... ...... ...... ......
2. 建立Line bot
Line bot 相對來說就是提供上一步的URL,有很多種方式可以呈現,像是透過 richmenu 或 關鍵字等等,在我這邊是可以透過輸入 gaming 或在 richmenu 點選,感覺LIFF還可以有很多種應用,等之後有空再來仔細來研究還有沒有什麼其他有趣的應用!
* 我這邊資料庫是連結自己搭建的MongoDB
Line Bot.py
""" Created on Tue Sep 17 20:25:25 2019 deploy this application to heroku at proposalbot @author: kevin """ #....import your module........ app = Flask(__name__) line_bot_api = LineBotApi(os.getenv('Channel_access_token',None)) handler = WebhookHandler(os.getenv('Channel_secret', None)) @app.route("/callback", methods=['POST']) def callback(): print('----------in-------') # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # handle webhook body try: handler.handle(body,signature) except LineBotApiError as e: print("Catch exception from LINE Messaging API: %s\n" % e.message) for m in e.error.details: print("ERROR is %s: %s" % (m.property, m.message)) print("\n") except InvalidSignatureError: abort(400) return 'OK' # def init_richmenu(event): # _id = os.getenv('first_richmenu', None) # print(_id) # line_bot_api.link_rich_menu_to_user(event.source.user_id, _id) @handler.add(MessageEvent, message=TextMessage) def handle_msg_text(event): # init_richmenu(event) message = event.message.text profile = line_bot_api.get_profile(event.source.user_id) mongo = MongoOperator(profile, message) chat = mongo.checkChat() #沒有chat就return None mongo.profileExist() if message == 'getRiddle': items = mongo.gettGuess() print(len(items)) r = random.randint(0, len(items)-1) print(r) t = items[r] line_bot_api.reply_message( event.reply_token, TextSendMessage(text=t) ) if message == 'set menu': _id = os.getenv('first_richmenu', None) print(_id) line_bot_api.link_rich_menu_to_user(event.source.user_id, _id) line_bot_api.reply_message( event.reply_token, TextSendMessage(text = 'set') ) elif event.message.text.lower() == 'all_richmenu': t= '' rich_menu_list = line_bot_api.get_rich_menu_list() for rich_menu in rich_menu_list: print(rich_menu.rich_menu_id) t += rich_menu.rich_menu_id line_bot_api.reply_message( event.reply_token, TextSendMessage(text=t) ) elif event.message.text.lower() == 'gaming': items = mongo.gettGuess() # items = ["多拉a夢", "熊", "魚"] r = random.randint(0, len(items) - 1) item = items[r] item = "多拉a夢" buttons_template = TemplateSendMessage( alt_text='play gmae with friend', template=ButtonsTemplate( title='Play gmae with friend', text='Have fun drawing and guessing according to following topic', thumbnail_image_url='https://i.imgur.com/TPELVeP.jpg', actions=[ MessageTemplateAction( label='需要畫的題目', text= item ), URITemplateAction( label='開始畫', uri='line://app/1622537665-your_URL' ) ] ) ) line_bot_api.reply_message(event.reply_token, buttons_template) else: token = Token() tk = token.calculate_token(message) print(tk) #sl是來源語言(Source language) ; tl是目標語言(Target language) 來源可以設定auto,但目標語言一定要設定一個特定語言->繁體中文為zh-TW 英文為en url = 'https://translate.google.com/translate_a/single?client=webapp&sl=auto&tl=en&hl=zh-TW&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&source=bh&ssel=0&tsel=0&kc=1&tk={}&q={}'.format(tk, quote(message)) headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'} req = requests.get(url=url, headers=headers) data = req.json() response = data[0][0][0] line_bot_api.reply_message( event.reply_token, TextSendMessage(text = response) ) if __name__ == "__main__": port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)
Demo:
畫畫部分:
翻譯部分:
Reference:
https://www.w3schools.com/jquery/jquery_get_started.asp
https://www.wibibi.com/info.php?tid=388
http://www.wibibi.com/info.php?tid=117
https://stackoverflow.com/questions/2368784/draw-on-html5-canvas-using-a-mouse
https://pydoing.blogspot.com/2011/09/javascript-onclick.html
Ajax相關:
https://ithelp.ithome.com.tw/articles/10200409