main.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "os"
  7. "os/signal"
  8. "path/filepath"
  9. "strings"
  10. "time"
  11. "github.com/eryajf/chatgpt-dingtalk/pkg/dingbot"
  12. "github.com/eryajf/chatgpt-dingtalk/pkg/logger"
  13. "github.com/eryajf/chatgpt-dingtalk/pkg/process"
  14. "github.com/eryajf/chatgpt-dingtalk/public"
  15. "github.com/xgfone/ship/v5"
  16. )
  17. func init() {
  18. public.InitSvc()
  19. logger.InitLogger(public.Config.LogLevel)
  20. }
  21. func main() {
  22. Start()
  23. }
  24. func Start() {
  25. app := ship.Default()
  26. app.Route("/").POST(func(c *ship.Context) error {
  27. var msgObj dingbot.ReceiveMsg
  28. err := c.Bind(&msgObj)
  29. if err != nil {
  30. return ship.ErrBadRequest.New(fmt.Errorf("bind to receivemsg failed : %v", err))
  31. }
  32. // 先校验回调是否合法
  33. if !public.CheckRequest(c.GetReqHeader("timestamp"), c.GetReqHeader("sign")) && msgObj.SenderStaffId != "" {
  34. logger.Warning("该请求不合法,可能是其他企业或者未经允许的应用调用所致,请知悉!")
  35. return nil
  36. } else if !public.JudgeOutgoingGroup(msgObj.ConversationID) && msgObj.SenderStaffId == "" {
  37. logger.Warning("该请求不合法,可能是未经允许的普通群outgoing机器人调用所致,请知悉!")
  38. return nil
  39. }
  40. // 再校验回调参数是否有价值
  41. if msgObj.Text.Content == "" || msgObj.ChatbotUserID == "" {
  42. logger.Warning("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题")
  43. return ship.ErrBadRequest.New(fmt.Errorf("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题"))
  44. }
  45. // 去除问题的前后空格
  46. msgObj.Text.Content = strings.TrimSpace(msgObj.Text.Content)
  47. if public.JudgeSensitiveWord(msgObj.Text.Content) {
  48. logger.Info(fmt.Sprintf("🙋 %s提问的问题中包含敏感词汇,userid:%#v,消息: %#v", msgObj.SenderNick, msgObj.SenderStaffId, msgObj.Text.Content))
  49. _, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您提问的问题中包含敏感词汇,请审核自己的对话内容之后再进行!**")
  50. if err != nil {
  51. logger.Warning(fmt.Errorf("send message error: %v", err))
  52. return err
  53. }
  54. return nil
  55. }
  56. // 打印钉钉回调过来的请求明细,调试时打开
  57. logger.Debug(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj))
  58. if public.Config.ChatType != "0" && msgObj.ConversationType != public.Config.ChatType {
  59. logger.Info(fmt.Sprintf("🙋 %s使用了禁用的聊天方式", msgObj.SenderNick))
  60. _, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,管理员禁用了这种聊天方式,请选择其他聊天方式与机器人对话!**")
  61. if err != nil {
  62. logger.Warning(fmt.Errorf("send message error: %v", err))
  63. return err
  64. }
  65. return nil
  66. }
  67. // 查询群ID,发送指令后,可通过查看日志来获取
  68. if msgObj.ConversationType == "2" && msgObj.Text.Content == "群ID" {
  69. if msgObj.RobotCode == "normal" {
  70. logger.Info(fmt.Sprintf("🙋 outgoing机器人 在『%s』群的ConversationID为: %#v", msgObj.ConversationTitle, msgObj.ConversationID))
  71. } else {
  72. logger.Info(fmt.Sprintf("🙋 企业内部机器人 在『%s』群的ConversationID为: %#v", msgObj.ConversationTitle, msgObj.ConversationID))
  73. }
  74. //_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), msgObj.ConversationID)
  75. if err != nil {
  76. logger.Warning(fmt.Errorf("send message error: %v", err))
  77. return err
  78. }
  79. return nil
  80. }
  81. // 不在允许群组,不在允许用户(包括在黑名单),满足任一条件,拒绝会话;管理员不受限制
  82. if msgObj.ConversationType == "2" && !public.JudgeGroup(msgObj.ConversationID) && !public.JudgeAdminUsers(msgObj.SenderStaffId) && msgObj.SenderStaffId != "" {
  83. logger.Info(fmt.Sprintf("🙋『%s』群组未被验证通过,群ID: %#v,userid:%#v, 昵称: %#v,消息: %#v", msgObj.ConversationTitle, msgObj.ConversationID, msgObj.SenderStaffId, msgObj.SenderNick, msgObj.Text.Content))
  84. _, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,该群组未被认证通过,无法使用机器人对话功能。**\n>如需继续使用,请联系管理员申请访问权限。")
  85. if err != nil {
  86. logger.Warning(fmt.Errorf("send message error: %v", err))
  87. return err
  88. }
  89. return nil
  90. } else if !public.JudgeUsers(msgObj.SenderStaffId) && !public.JudgeAdminUsers(msgObj.SenderStaffId) && msgObj.SenderStaffId != "" {
  91. logger.Info(fmt.Sprintf("🙋 %s身份信息未被验证通过,userid:%#v,消息: %#v", msgObj.SenderNick, msgObj.SenderStaffId, msgObj.Text.Content))
  92. _, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您的身份信息未被认证通过,无法使用机器人对话功能。**\n>如需继续使用,请联系管理员申请访问权限。")
  93. if err != nil {
  94. logger.Warning(fmt.Errorf("send message error: %v", err))
  95. return err
  96. }
  97. return nil
  98. }
  99. if len(msgObj.Text.Content) == 0 || msgObj.Text.Content == "帮助" {
  100. // 欢迎信息
  101. _, err := msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), public.Config.Help)
  102. if err != nil {
  103. logger.Warning(fmt.Errorf("send message error: %v", err))
  104. return ship.ErrBadRequest.New(fmt.Errorf("send message error: %v", err))
  105. }
  106. } else {
  107. logger.Info(fmt.Sprintf("🙋 %s发起的问题: %#v", msgObj.SenderNick, msgObj.Text.Content))
  108. // 除去帮助之外的逻辑分流在这里处理
  109. switch {
  110. case strings.HasPrefix(msgObj.Text.Content, "#图片"):
  111. return process.ImageGenerate(&msgObj)
  112. case strings.HasPrefix(msgObj.Text.Content, "#查对话"):
  113. return process.SelectHistory(&msgObj)
  114. case strings.HasPrefix(msgObj.Text.Content, "#域名"):
  115. return process.DomainMsg(&msgObj)
  116. case strings.HasPrefix(msgObj.Text.Content, "#证书"):
  117. return process.DomainCertMsg(&msgObj)
  118. default:
  119. msgObj.Text.Content, err = process.GeneratePrompt(msgObj.Text.Content)
  120. // err不为空:提示词之后没有文本 -> 直接返回提示词所代表的内容
  121. if err != nil {
  122. _, err = msgObj.ReplyToDingtalk(string(dingbot.TEXT), msgObj.Text.Content)
  123. if err != nil {
  124. logger.Warning(fmt.Errorf("send message error: %v", err))
  125. return err
  126. }
  127. return nil
  128. }
  129. return process.ProcessRequest(&msgObj)
  130. }
  131. }
  132. return nil
  133. })
  134. // 解析生成后的图片
  135. app.Route("/images/:filename").GET(func(c *ship.Context) error {
  136. filename := c.Param("filename")
  137. root := "./data/images/"
  138. return c.File(filepath.Join(root, filename))
  139. })
  140. // 解析生成后的历史聊天
  141. app.Route("/history/:filename").GET(func(c *ship.Context) error {
  142. filename := c.Param("filename")
  143. root := "./data/chatHistory/"
  144. return c.File(filepath.Join(root, filename))
  145. })
  146. // 直接下载文件
  147. app.Route("/download/:filename").GET(func(c *ship.Context) error {
  148. filename := c.Param("filename")
  149. root := "./data/chatHistory/"
  150. return c.Attachment(filepath.Join(root, filename), "")
  151. })
  152. // 服务器健康检测
  153. app.Route("/").GET(func(c *ship.Context) error {
  154. //返回消息优雅一点,告诉用户欢迎使用ding ding机器人服务 服务状态oK
  155. return c.JSON(http.StatusOK, map[string]interface{}{
  156. "status": "ok",
  157. "msg": "欢迎使用钉钉机器人",
  158. })
  159. })
  160. port := ":" + public.Config.Port
  161. srv := &http.Server{
  162. Addr: port,
  163. Handler: app,
  164. }
  165. // Initializing the server in a goroutine so that
  166. // it won't block the graceful shutdown handling below
  167. go func() {
  168. logger.Info("🚀 The HTTP Server is running on", port)
  169. if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  170. logger.Fatal("listen: %s\n", err)
  171. }
  172. }()
  173. // Wait for interrupt signal to gracefully shutdown the server with
  174. // a timeout of 5 seconds.
  175. quit := make(chan os.Signal, 1)
  176. // kill (no param) default send syscall.SIGTERM
  177. // kill -2 is syscall.SIGINT
  178. // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
  179. // signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  180. signal.Notify(quit, os.Interrupt)
  181. <-quit
  182. logger.Info("Shutting down server...")
  183. // 5秒后强制退出
  184. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  185. defer cancel()
  186. if err := srv.Shutdown(ctx); err != nil {
  187. logger.Fatal("Server forced to shutdown:", err)
  188. }
  189. logger.Info("Server exiting!")
  190. }