Prepare Account
- Official account certified by WeChat (requires customer service information): https://mp.weixin.qq.com/
- FastGpt account, and knowledge base: https://fastgpt.run.
- Laf account: https://laf.run/
Step1: Laf preparation
1. Enter Laf official website and register an account
2. Create a new application and directly create a free one for testing
3. Copy code
Copy directly, no need to change anything first
import cloud from '@lafjs/cloud'; import * as crypto from 'crypto'; // official account configuration const appid = 'wxb1833715d8f0809d' const appsecret = 'fd76ce714a8083112100c2160b2f2c5d' const wxToken = 'test'; // fastgpt configuration const apikey = "63f9a14228d2a688d8dc9e1b-skmzjonmv1gyno2iyky1z" const modelId = "642adec15f01d67d4613efdb" // Create a database connection and get the Message collection const db = cloud. database(); const_ = db.command const Message = db. collection('messages') // Process the received WeChat official account message export async function main(event) {<!-- --> // const res = await cloud.fetch.post(`https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${<!-- -->await getAccess_token()}` , {<!-- --> // button: [ // {<!-- --> // "type": "click", // "name": "Clear Record", // "key": "CLEAR" // }, // ] // }) const {<!-- --> signature, timestamp, nonce, echostr } = event.query; // Verify whether the message is legal, if not, return an error message if (!verifySignature(signature, timestamp, nonce, wxToken)) {<!-- --> return 'Invalid signature'; } // If it is the first verification, return echostr to the WeChat server if (echostr) {<!-- --> return echostr; } // -------------- text begins const payload = event.body.xml; const sessionId = payload. fromusername[0] console. log(payload) // Click to clear the record if (payload.msgtype[0] === 'event' & amp; & amp; payload.eventkey[0] === 'CLEAR') {<!-- --> console.log(1111) await Message.where({<!-- --> sessionId: sessionId }).remove({<!-- --> multi: true }) await replyBykefu('The record has been cleared', sessionId) return 'clear record' } // Just do text message example if (payload.msgtype[0] !== 'text') return 'no text' const newMessage = {<!-- --> msgid: payload.msgid[0], question: payload. content[0]. trim(), username: payload. fromusername[0], sessionId, createdAt: Date.now() } await replyText(newMessage, payload. fromusername[0]) return 'success' } // handle the text reply message async function replyText(message, touser) {<!-- --> const {<!-- --> question, sessionId, msgid } = message; // Duplicate content, do not reply const {<!-- --> data: msg } = await Message.where({<!-- --> msgid: message.msgid }).getOne() if (msg) return console.log("received user message", touser, message) // Immediately add a record to be replied await Message.add(message); // reply prompt await replyBykefu("The robot is thinking...", sessionId) await changesState(sessionId) const reply = await getFastGptReply(question, sessionId); const {<!-- --> answer } = reply; await Message.where({<!-- --> msgid: message.msgid }).update({<!-- --> answer, }); // return answer; await reply Bykefu(answer, touser) } // Get WeChat public account ACCESS_TOKEN async function getAccess_token() {<!-- --> const shared_access_token = await cloud. shared. get("mp_access_token") if (shared_access_token) {<!-- --> if (shared_access_token.exp > Date.now()) {<!-- --> return shared_access_token.access_token } } // ACCESS_TOKEN does not exist or has expired // Get WeChat public account ACCESS_TOKEN const mp_access_token = await cloud.fetch.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential & amp;appid=${<!-- -->appid} & amp ;secret=${<!-- -->appsecret}`) cloud.shared.set("mp_access_token", {<!-- --> access_token: mp_access_token.data.access_token, exp: Date.now() + 7100 * 1000 }) return mp_access_token.data.access_token } // official account customer service reply text message export async function replyBykefu(message, touser) {<!-- --> // Determine whether it is a Chinese character function isChinese(char) {<!-- --> return /[\\一-\\龥]/.test(char) // determine whether it is a Chinese character } // split text length function splitText(text) {<!-- --> let result = [] let len = text. length let index = 0 while (index < len) {<!-- --> let part = '' let charCount = 0 while (charCount < 800 & amp; & amp; index < len) {<!-- --> let char = text[index] charCount++ part += char if (isChinese(char)) charCount + + // Chinese character count + 1 index + + } result. push(part) } return result } // define sleep function function sleep(ms) {<!-- --> return new Promise(resolve => setTimeout(resolve, ms)) }; const access_token = await getAccess_token() let text = splitText(message) let len = splitText(message).length try{<!-- --> for (let i = 0; i < len; i ++ ) {<!-- --> let part = text[i] // Get the i-th paragraph await sleep(1000) // Reply message const res = await cloud.fetch.post(`https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${<!-- -->access_token}`, {< !-- --> "touser": touser, "msgtype": "text", "text": {<!-- --> "content": part } }) } }catch(err) {<!-- --> console. log(err) } } // Modify the official account reply status export async function changesState(touser) {<!-- --> const access_token = await getAccess_token() // Modify the state being entered const res = await cloud.fetch.post(`https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token=${<!-- -->access_token}`, {< !-- --> "touser": touser, "command": "Typing" }) } // Verify that the message sent by the WeChat server is legal export function verifySignature(signature, timestamp, nonce, token) {<!-- --> const arr = [token, timestamp, nonce].sort(); const str = arr. join(''); const sha1 = crypto. createHash('sha1'); sha1. update(str); return sha1.digest('hex') === signature; } // return assembly xml export function toXML(payload, content) {<!-- --> const timestamp = Date.now(); const {<!-- --> tousername: fromUserName, fromusername: toUserName } = payload; return ` <xml> <ToUserName><![CDATA[${<!-- -->toUserName}]]></ToUserName> <FromUserName><![CDATA[${<!-- -->fromUserName}]]></FromUserName> <CreateTime>${<!-- -->timestamp}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[${<!-- -->content}]]></Content> </xml> ` } // call fastgpt answer async function getFastGptReply(question, sessionId) {<!-- --> const res = await db. collection('messages') .where({<!-- --> sessionId }) .get() // Get up to 10 sets of context const list = res.data.slice(-10) const prompts = list. map((item) => [{<!-- --> obj: "Human", value: item.question || '' }, {<!-- --> obj: "AI", value: item.answer || '' }]).concat({<!-- --> obj: "Human", value: question }).flat() const config = {<!-- --> method: 'post', // set the request method to POST url: 'https://fastgpt.run/api/openapi/chat/chat', // set request address headers: {<!-- --> // Set request header information apikey, 'Content-Type': 'application/json' }, data: {<!-- --> // Set request body data modelId, isStream: false, prompts } } try {<!-- --> const ret = await cloud.fetch(config) console.log("fastgpt response", ret.data) return {<!-- --> answer: ret.data.data || ret.data || '' } } catch (e) {<!-- --> console.log("Error", e.response) return {<!-- --> error: "The question is too difficult and there is an error. (uДu〃).", } } }
5. Click Publish
6. Copy address
Step2 official account preparation
It must be an official account after authentication, otherwise it will be invalid. I don’t have a certified number, so the video is shown with a test number. The following screenshots are the complete steps:
1. Set the laf address for the official account
2. Get wx appId and secret
3. Verify laf address
Fill in the 3 contents obtained in the previous two steps into laf
Step3 FastGpt preparation
1. Get apikey
2. Get modelId
3. Replace the Laf variable
Test
Send a message to the official account to see if there is a reply.
QA
No response after sending message
Go to the laf log first to check whether you have received user messages, and the following prompts indicate that it is normal. You may need to click search to refresh it.
If you receive a message but do not reply, it is likely that the official account does not have permission to send customer service messages. Corresponding to the permissions in the figure below