当前位置:   article > 正文

AI接入微信公众号方法总结_微信公众号 集成ai

微信公众号 集成ai

AI挺好用的。但是想用时就得打开网页或者其他工具插件才行?太不方便了。记得有次要给媳妇演示下它的强大,竟没带电脑竟啥事干不成。索性把AI接入微信公众号,这样在公众号聊天窗口里发消息,AI自动给我回复内容,且可以分享给好友邀好友一块儿体验,这太好玩儿了。

本文主要来聊聊如何快速使用个人微信接入 AI,欢迎体验,仅供学习参考。

好玩多了,AI接入微信公众号后可以随时随地使用它了。这里总结下AI接入微信的方法。效果展示:

 环境准备

1.微信公众号。测试的就行,也建议用测试的,很容易申请,无门槛且不限制次数。

2.AI的访问api-key,这个需要科学上网申请个账号,获取api-key并保存好。

3.一台云服务器。各大云商有提供,包年很便宜,部署下Golang的后台服务。

具备以上条件就ok啦,接下来介绍下微信公众号接入方法。

我的首页里关注公众号可体验功能。测试号支持最多一百个用户,名额有限,目前还有剩余可以体验,欢迎交流。源码下载地址:https://download.csdn.net/download/qq8864/87448657

实现思路

总体方案介绍

通过搭建后台云服务,实现微信公众号与AI的连接。使用微信公众号开发平台提供的接口,主要是客服消息接口,用户在公众号聊天窗口发送问题内容,后台服务收到后,调用ai接口问答,并将AI的结果发送到微信公众号窗口中进行展示。

接入微信公众平台

如何接入微信平台?这部分内容网上有很多,比较简单这里就不详细介绍了。

介绍几个我封装的接入微信公众号的golang接口:

  1. // HandleWxLogin首次接入,成为开发者
  2. func HandleWxLogin(c *gin.Context) {
  3. fmt.Printf("==>HandleWxLogin\n")
  4. echostr := c.DefaultQuery("echostr", "")
  5. if echostr != "" {
  6. fmt.Printf("==>echostr:%s\n", echostr)
  7. c.String(200, "%s", echostr)
  8. return
  9. }
  10. }
  11. // WxGetAccessToken 获取微信accesstoken
  12. func WxGetAccessToken() string {
  13. url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%v&secret=%v", APPID, APPSECRET)
  14. resp, err := http.Get(url)
  15. if err != nil {
  16. fmt.Println("获取微信token失败", err)
  17. return ""
  18. }
  19. defer resp.Body.Close()
  20. body, err := ioutil.ReadAll(resp.Body)
  21. if err != nil {
  22. fmt.Println("微信token读取失败", err)
  23. return ""
  24. }
  25. token := token{}
  26. err = json.Unmarshal(body, &token)
  27. if err != nil {
  28. fmt.Println("微信token解析json失败", err)
  29. return ""
  30. }
  31. return token.AccessToken
  32. }

客服回复消息接口

调用此封装的微信接口,向用户回复消息。比如用户在公众号聊天窗口发送问题后,通过向AI的api接口发送请求,把AI回复的内容,通过客服回复消息接口向用户应答。

微信客服回复接口封装

  1. // 客服回复接口
  2. func WxPostCustomTextMsg(accessToken string, touser string, content string) {
  3. url := "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + accessToken
  4. req:=&WxCustomTextMsg{ToUser:touser,MsgType:"text",Text:WxCustomText{Content:content}}
  5. jsonStr := req.ToJson()
  6. //fmt.Printf("WxPostCustomTextMsg:%#v\n", jsonStr)
  7. request ,_:= http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
  8. request.Header.Set("Content-Type", "application/json")
  9. client := &http.Client{}
  10. resp, err := client.Do(request)
  11. if err != nil {
  12. fmt.Println(err)
  13. return
  14. }
  15. defer resp.Body.Close()
  16. body, err := ioutil.ReadAll(resp.Body)
  17. if err != nil {
  18. fmt.Println(err)
  19. return
  20. }
  21. fmt.Println(string(body))
  22. }

需要注意的是,需要使用golang的协程异步处理,因为用户发送信息后若5秒内没响应,会报服务异常的。异步示例如下:

  1. var wxReceiveFunc = func(msg wxapi.WxReceiveCommonMsg) error {
  2. fmt.Println("weixin msg received")
  3. fmt.Printf("%#v\n", msg)
  4. touser := msg.FromUserName
  5. content := msg.Content
  6. accessToken := wxapi.WxGetAccessToken()
  7. //异步请求chatAI,成功后调用客服接口回复
  8. go func(){
  9. resp:=chatapi.AskChatAI(content)
  10. if(resp !=""){
  11. wxapi.WxPostCustomTextMsg(accessToken,touser,resp)
  12. }else{
  13. wxapi.WxPostCustomTextMsg(accessToken,touser,"chatGPT服务异常")
  14. }
  15. }()
  16. return nil
  17. }

text-davinci-003介绍

使用text-davinci-003的模型的ai的接口,详细的介绍参照地址:

https://beta.openai.com/docs/api-reference/completions/create#completions/create-model 

关于text-davinci-003和官方AI的区别,参照下面文章,其实用起来差别不大。甚至text-davinci-003在写作创造方面更优秀。简单说AI是个产品,text-davinci-003更面向开发者开发产品使用。

  1. curl https://api.openai.com/v1/completions \
  2. -H "Content-Type: application/json" \
  3. -H "Authorization: Bearer YOUR_API_KEY" \
  4. -d '{"model": "text-davinci-003", "prompt": "Say this is a test", "temperature": 0, "max_tokens": 7}'
  1. {
  2. "id": "cmpl-uqkvlQyYK7bGYrRHQ0eXlWi7",
  3. "object": "text_completion",
  4. "created": 1589478378,
  5. "model": "text-davinci-003",
  6. "choices": [
  7. {
  8. "text": "\n\nThis is indeed a test",
  9. "index": 0,
  10. "logprobs": null,
  11. "finish_reason": "length"
  12. }
  13. ],
  14. "usage": {
  15. "prompt_tokens": 5,
  16. "completion_tokens": 7,
  17. "total_tokens": 12
  18. }
  19. }

官方貌似只提供了python的demo和接口实现,其实模拟http调用使用其他语言都可以实现。

main.go代码实现

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "weixin2/apis"
  6. "weixin2/wxapi"
  7. _"weixin2/convert"
  8. "weixin2/chatapi"
  9. "github.com/gin-gonic/gin"
  10. "github.com/jasonlvhit/gocron"
  11. )
  12. var wxReceiveFunc = func(msg wxapi.WxReceiveCommonMsg) error {
  13. fmt.Println("weixin msg received")
  14. fmt.Printf("%#v\n", msg)
  15. touser := msg.FromUserName
  16. content := msg.Content
  17. accessToken := wxapi.WxGetAccessToken()
  18. go func(){
  19. resp:=chatapi.AskChatAI(content)
  20. if(resp !=""){
  21. wxapi.WxPostCustomTextMsg(accessToken,touser,resp)
  22. }else{
  23. wxapi.WxPostCustomTextMsg(accessToken,touser,"chatGPT服务异常")
  24. }
  25. }()
  26. return nil
  27. }
  28. func main() {
  29. // 定时每天早上7点 公众号发送天气预报
  30. gocron.Every(1).Day().At("07:00").Do(apis.SendWeather)
  31. // 定时每天早上九点 公众号发送每日一句
  32. gocron.Every(1).Day().At("10:00").Do(apis.SendExponent)
  33. fmt.Println("开启定时触发任务")
  34. gocron.Start()
  35. wxapi.WxReceiveFunc = wxReceiveFunc
  36. router := gin.Default()
  37. router.GET("/", wxapi.HandleWxLogin)
  38. router.POST("/", wxapi.HandleWxPostRecv)
  39. //运行的端口
  40. router.Run(":8000")
  41. }

至于里面的每日发送是啥?

哈哈,为了好玩。我让每日7点给我发送一份天气预报。每日12点发个当前大盘点位,关注下股市大盘。至于大盘咨询的获取的实现,调用的第三方的接口。这里也给出下。

大盘信息获取接口实现:

  1. package apis
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "net/http"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "weixin2/util"
  10. "weixin2/wxapi"
  11. )
  12. var (
  13. LastPoint string //上次点数
  14. LastState string //上次状态
  15. LastTime string //上次时间
  16. State string //当前状态
  17. )
  18. type ExponentInfo struct {
  19. CurrentPoints string // 当前点数
  20. Gszzl string //涨跌幅度
  21. Turnover string //成交额(万元)
  22. Name string //名称
  23. }
  24. // 查询指数
  25. func QueryBroadMarket(code string) (exponentInfo ExponentInfo, err error) {
  26. res, err := http.Get("http://hq.sinajs.cn/list=" + code)
  27. if res != nil {
  28. defer res.Body.Close()
  29. }
  30. if err != nil {
  31. return
  32. }
  33. if res.StatusCode != http.StatusOK {
  34. return exponentInfo, fmt.Errorf("http statusCode:%d", res.StatusCode)
  35. }
  36. result, err := ioutil.ReadAll(res.Body)
  37. if err != nil {
  38. return exponentInfo, err
  39. }
  40. base := util.ParseStr(string(result))
  41. //exponentInfo.Name = BroadMarketName[code]
  42. exponentInfo.Name = util.ConvertToString(base[0], "gbk", "utf-8")
  43. currentPoints := strings.Split(base[1], ".")
  44. exponentInfo.CurrentPoints = currentPoints[0]
  45. exponentInfo.Gszzl = base[3]
  46. if len(base[5]) > 5 {
  47. base[5] = base[5][0 : len(base[5])-4]
  48. base[5] += "亿"
  49. }
  50. exponentInfo.Turnover = base[5]
  51. return
  52. }
  53. // ExponentTplID 模板ID
  54. // {{title.DATA}} {{date.DATA}} {{curpoint.DATA}} {{turnover.DATA}} {{gszzl.DATA}} {{state.DATA}} {{lastpoint.DATA}} {{laststate.DATA}} {{tips.DATA}}
  55. var ExponentTplID = "RIzqpuRxwlV1IWucjE186Uvu949N0Uly9H3CoTMGskc"
  56. // ExponentTpl 结构
  57. type ExponentTpl struct {
  58. Title string `json:"title"`
  59. Content string `json:"content"`
  60. Note string `json:"note"`
  61. }
  62. // AfterTime 判断一个时间在现在之前还是之后
  63. // layout 解析的时间格式,
  64. // judeTime 需要判断的时间
  65. // bool =true 在当前时候之后,false在当前时间之前
  66. func AfterTime(tpl, judeTime string) (bool, error) {
  67. stringToTime, err := time.Parse(tpl, judeTime)
  68. if err != nil {
  69. return false, err
  70. }
  71. beforeOrAfter := stringToTime.After(time.Now())
  72. if beforeOrAfter == true {
  73. return true, nil
  74. } else {
  75. return false, nil
  76. }
  77. }
  78. //SendExponent 发送每日大盘
  79. func SendExponent() {
  80. info, err := QueryBroadMarket("s_sh000001")
  81. if err != nil {
  82. fmt.Printf("QueryBroadMarket error,err=%s\n", err.Error())
  83. return
  84. }
  85. accessToken := wxapi.WxGetAccessToken()
  86. if accessToken == "" {
  87. return
  88. }
  89. flist := wxapi.WxGetUserList(accessToken)
  90. if flist == nil {
  91. return
  92. }
  93. title := "上证指数"
  94. timeStr := time.Now().Format("2006-01-02 15:04:05")
  95. tips := "tips:市场有风险,入市需谨慎"
  96. lastflt, err := strconv.ParseFloat(LastPoint, 32/64)
  97. curflt, err := strconv.ParseFloat(info.CurrentPoints, 32/64)
  98. if curflt > lastflt {
  99. State = "上升 " + strconv.FormatFloat(curflt-lastflt, 'f', 2, 32)
  100. } else {
  101. State = "下降 " + strconv.FormatFloat(curflt-lastflt, 'f', 2, 32)
  102. }
  103. reqdata := "{\"title\":{\"value\":\"" + title + "\", \"color\":\"#0000CD\"},\"date\":{\"value\":\"" + timeStr + "\"},\"curpoint\":{\"value\":\"" + "当前点数:" + info.CurrentPoints + "\"}, \"turnover\":{\"value\":\"" + "成交额:" + info.Turnover + "\"},\"gszzl\":{\"value\":\"" + "估值:" + info.Gszzl + "\"},\"state\":{\"value\":\"" + "今日状态:" + State + "\"},\"lastpoint\":{\"value\":\"" + "昨日点数:" + LastPoint + "\"},\"laststate\":{\"value\":\"" + "昨日状态:" + LastState + "\"},\"tips\":{\"value\":\"" + tips + "\"}}"
  104. for _, v := range flist {
  105. wxapi.WxPostTemplate(accessToken, reqdata, "blog.csdn.net/qq8864", ExponentTplID, v.Str)
  106. }
  107. now := time.Now()
  108. if now.Hour() > 13 {
  109. LastTime = timeStr
  110. LastPoint = info.CurrentPoints
  111. LastState = State
  112. }
  113. }

每日天气接口实现: 

  1. /**
  2. 每日天气api接口
  3. */
  4. package apis
  5. import (
  6. "fmt"
  7. "github.com/tidwall/gjson"
  8. "io/ioutil"
  9. "net/http"
  10. "weixin2/wxapi"
  11. )
  12. var (
  13. WeatTemplateID = "AXCDf6fetE2-ebF7Di8izW9avSN4cFWvnfcAQDSW7-k" //天气模板ID
  14. WeatherApiVersion = "v6"
  15. WeatherAPPID = "45849795"
  16. WeatherAppSECRET = "T5xyVwLB"
  17. )
  18. // GetWeather 获取天气
  19. func GetWeather(city string) (string, string, string, string) {
  20. url := fmt.Sprintf("https://tianqiapi.com/api?version=%s&appid=%s&appsecret=%s&city=%s", WeatherApiVersion, WeatherAPPID, WeatherAppSECRET, city)
  21. resp, err := http.Get(url)
  22. if err != nil {
  23. fmt.Println("获取天气失败", err)
  24. return "", "", "", ""
  25. }
  26. defer resp.Body.Close()
  27. body, err := ioutil.ReadAll(resp.Body)
  28. if err != nil {
  29. fmt.Println("读取内容失败", err)
  30. return "", "", "", ""
  31. }
  32. fmt.Println(string(body))
  33. data := string(body)
  34. day := gjson.Get(data, "date").Str
  35. wea := gjson.Get(data, "wea").Str
  36. tem := gjson.Get(data, "tem").Str
  37. //tem2 := gjson.Get(thisday, "tem2").Str
  38. airTips := gjson.Get(data, "air_tips").Str
  39. return day, wea, tem, airTips
  40. }
  41. // PostWeather 发送天气
  42. func PostWeather(accessToken, city, openid string) {
  43. day, wea, tem, airTips := GetWeather(city)
  44. if day == "" || wea == "" || tem == "" || airTips == "" {
  45. return
  46. }
  47. reqdata := "{\"city\":{\"value\":\"城市:" + city + "\", \"color\":\"#0000CD\"}, \"day\":{\"value\":\"" + day + "\"}, \"wea\":{\"value\":\"天气:" + wea + "\"}, \"tem1\":{\"value\":\"平均温度:" + tem + "\"}, \"air_tips\":{\"value\":\"tips:" + airTips + "\"}}"
  48. //fmt.Println(reqdata)
  49. wxapi.WxPostTemplate(accessToken, reqdata, "http://yangqq.xyz", WeatTemplateID, openid)
  50. }
  51. //SendWeather 向公众号所有用户发送天气预报
  52. func SendWeather() {
  53. accessToken := wxapi.WxGetAccessToken()
  54. if accessToken == "" {
  55. return
  56. }
  57. flist := wxapi.WxGetUserList(accessToken)
  58. if flist == nil {
  59. return
  60. }
  61. city := "郑州"
  62. for _, v := range flist {
  63. go PostWeather(accessToken, city, v.Str)
  64. }
  65. }

其他资源

ChatGPT API版介绍及使用讲解。 - 哔哩哔哩

ChatGPT又添劲敌?OpenAI核心员工创业,新模型获一片叫好

百度安全验证

【NLP】万字拆解!追溯ChatGPT各项能力的起源_qq62985c01d4e12的技术博客_51CTO博客

https://www.wpsshop.cn/w/羊村懒王/article/detail/317879

推荐阅读
相关标签
  

闽ICP备14008679号