本文章同时发布于:
MediumiT邦帮忙大家好,继上次Week21 - 用Redis来帮Line bot髒沙发设计一次性功能 - 概念篇后,这次要来说明实作的部分。
你可能是先要先知道
Line bot串接: 可以参考twtrubiks大大的line-bot-tutorialDocker: 也可参考twtrubiks大大的docker-tutorial因为这篇文章会着重在Redis
,所以Line bot的串接细节就不多着墨。
并且为求方便快速,Redis
也会透过Docker
来直接产生,如果大家不太知道Docker
是什么的话,广义简单的来说,他就是一个「秒启动的虚拟机」。
Docker
透过把实机的核心挂载至一个隔离环境来当成虚拟机,此虚拟机不用透过Hypervisor
与实机沟通,运作成本非常小,所以效能与启动速度跟实机差不多。
听起来很複杂?没关係你可以看twtrubiks大大的文章或者就直接把它当成一个超强超快的虚拟机就对了XD
整体流程与格式
使用者Line-APP
传送的讯息与图片,Line-Server
都会打一个API
给我们指定的My-Server
,我们俗称Webhook
。
所以我们只需实作一个POST API api/WebhookHanlder
来接受Line Server
的Webhook
,即可模拟实际状况。
格式
# POST API api/WebhookHanlder# API-1{ "groupID": "69617aa7-9512-44c3-9e10-1626cc8bf585", "type": "message", "data": { "text": "辨识" }}# POST API api/WebhookHanlder# API-2{ "groupID": "69617aa7-9512-44c3-9e10-1626cc8bf585", "type": "image", "data": { "imageID": "9b2fcfb3-e39c-4ea2-96a7-a3fb408095d0" }}# Redis-1key: 3e6e3637-45da-458f-8ade-85b8b9f0af8a:search // 为GroupID+typevalue: true
实作
完整程式码在此: w3school-40-weeks
解释我会在程式码旁边注释。
首先我们先进入week22
的资料夹,
$ cd week22
利用docker-compose
来启动Redis
$ docker-compose up
version: '3.5'services: redis: image: redis:alpine command: redis-server --appendonly yes ports: - 6379:6379 # 将docker内的Redis的6379 port对应到实机6379 port volumes: - ./data:/data restart: always
启动Server
$ go run main.go
主要的main function
将Redis
初始化,并设置API
func initRedis() *libredis.Client {option, err := libredis.ParseURL("redis://localhost:6379/0")check(err)client := libredis.NewClient(option)return client}func main() {r := gin.Default()redisClient = initRedis() // 初始化Redisr.POST("/api/WebhookHanlder", webhookHanlder) // 新增POST API api/WebhookHanlderr.Run()}
由webhookHanlder function
来处理POST API api/WebhookHanlder
的请求,并且会比对json body
里type
是message
还是image
来换到textHandler function
或imageHandler function
来处理。
// Controllerfunc webhookHanlder(c *gin.Context) {var postType PostTypereadBody(c, &postType) // 读取request的body,并把struct套上,这个PostType struct是专门用来解析request type的switch postType.Type { // 如果是message type就以textHandler function处理,如果是image type就以imageHandler function处理case "message":textHandler(c)case "image":imageHandler(c)}}func textHandler(c *gin.Context) {var postMessage PostMessagereadBody(c, &postMessage) // 读取request的body,并把struct套上,这个PostMessage struct是专门用来读取message json的key := postMessage.GroupID + ":search" // 将GroupID+typeerr := redisClient.Set(ctx, key, true, 0).Err() // 设置key为GroupID+type,value为truecheck(err)err = redisClient.Expire(ctx, key, 24*time.Hour).Err() // 设置key-value的过期时间为24小时check(err)c.Status(204) // 回传204 response}func imageHandler(c *gin.Context) {isThereSearchRequest := func(redisGroupResult string, err error) bool {if err != nil || redisGroupResult == "" {return false}redisGroupResultBool, err := strconv.ParseBool(redisGroupResult)check(err)return redisGroupResultBool}var postImage PostImagereadBody(c, &postImage) // 读取request的body,并把struct套上,这个PPostImage struct是专门用来读取image json的key := postImage.GroupID + ":search" // 将GroupID+typeif isThereSearchRequest(redisClient.Get(ctx, key).Result()) { // 如果GroupID+type存在就回传女优资讯,不存在就回传「你没有要求辨识」的error messagec.JSON(200, gin.H{"AVStar": "佐仓绊",})return}c.JSON(403, gin.H{"error": "No search requested",})}
谢谢你的阅读,也欢迎分享讨论指正~
参考与引用资料
*Golang: Read from an io.ReadWriter without losing its content
line-bot-tutorialdocker-tutorial