最近很紅的ChatGPT,如果也能在Line上面使用的話,是不是感覺更方便呢?我們就來串接ChatGPT的開發公司OpenAI提供的API到Line Bot上吧!
這個範例會用Express起一個web server,建立一個method POST且path為/webhook的route作為line的webhook,用來接收line傳來的events
首先你需要先到 Line developers console 點選「Create a new channel」建立一個新的Channel,類型選擇「Messaging API」。填寫完成送出後進入Channel設定頁,複製最下方的「Channel secret」。回到上方選擇「Messaging API」頁籤,複製最下方的「Channel access token」。
用NPM安裝 openai, @line/bot-sdk, express,並在檔案中 require
const { Configuration, OpenAIApi } = require('openai') const line = require('@line/bot-sdk') const express = require('express')
建立Line API需要的Config,將剛剛Line Channel的Token跟Secret填入,範例以環境變數帶入。
// line config const lineConfig = { channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN, channelSecret: process.env.CHANNEL_SECRET }
建立express,設定line bot所需的webhook路由以及訊息事件的handler。將程式碼上到主機上後,就可以繼續完成Line Bot的設定了。
const app = express() // line webhook app.post('/webhook', line.middleware(lineConfig), (req, res) => { Promise .all(req.body.events.map(lineEventHandler)) .then((result) => res.json(result)) .catch((err) => { console.error(err) res.status(500).end() }) }) // line message handler function lineEventHandler (event) { const lineClient = new line.Client(lineConfig) return lineClient.replyMessage(event.replyToken, { type: 'text', text: 'Bot收到訊息囉' }) } app.listen(3000)
到 Line developers console 選取剛剛新增的Channel,點選「Messaging API」頁簽,在下方的「Webhook URL」點選「edit」,填入你的Webhook網址並按下「Update」,完成後按下「Verify」,就可以檢查webhook有沒有正常運作了。
接下來首先要到OpenAI 註冊你的帳號,註冊完後,到API Keys頁面,點選「Create new secret key」,複製API Key,回到程式碼,建立OpenAI API需要的Config填入剛剛的API Key,範例以環境變數帶入。
const openAiConfig = new Configuration({ apiKey: process.env.OPENAI_API_KEY, })
新增一個function負責傳遞訊息給OpenAI API,並等待回傳的訊息。
async function askOpenAI (question) { const openai = new OpenAIApi(openAiConfig) // request try { // 使用text-davinci-003 model // const completion = await openai.createCompletion({ // model: 'text-davinci-003', // 看你要使用哪個model: https://platform.openai.com/docs/models // max_tokens: 100, // prompt: question, // temperature: 0.1 // 這是一個 0 - 1 的數值,越靠近1回答會更多樣性,越靠近0回答會更可靠 // }) // return completion.data.choices[0].text.replace(/^\n+/g, '') // 使用gpt-3.5-turbo model (這個比較強) const completion = await openai.createChatCompletion({ model: 'gpt-3.5-turbo', messages: [{ role: 'user', content: question }] }) return completion.data.choices[0].message.content.replace(/^\n+/g, '') } catch (err) { console.log(err) return '抱歉我不知道' } }
將剛剛的Handler function改成這樣,注意function的前面有加上了 async
async function lineEventHandler (event) { const lineClient = new line.Client(lineConfig) if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null) } const result = await askOpenAI(event.message.text) // 將OpenAI回的訊息傳給使用者 return lineClient.replyMessage(event.replyToken, { type: 'text', text: result }) }
最後,因為我不想讓他每次都回我,尤其是如果將他加入群聊的時候,可能會很吵,因此我在Handler中再加入了一個判斷
if (event.type !== 'message' || event.message.type !== 'text') { return Promise.resolve(null) } // 改為 if (event.type !== 'message' || event.message.type !== 'text' || !event.message.text.includes('村長')) { return Promise.resolve(null) }
這樣子的話,就只有訊息中包含了村長兩個字時,才會將訊息傳給OpenAI等回應。