main.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  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. }
  20. func main() {
  21. Start()
  22. }
  23. func Start() {
  24. app := ship.Default()
  25. app.Route("/").POST(func(c *ship.Context) error {
  26. var msgObj dingbot.ReceiveMsg
  27. err := c.Bind(&msgObj)
  28. if err != nil {
  29. return ship.ErrBadRequest.New(fmt.Errorf("bind to receivemsg failed : %v", err))
  30. }
  31. if msgObj.Text.Content == "" || msgObj.ChatbotUserID == "" {
  32. logger.Warning("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题")
  33. return ship.ErrBadRequest.New(fmt.Errorf("从钉钉回调过来的内容为空,根据过往的经验,或许重新创建一下机器人,能解决这个问题"))
  34. }
  35. // 打印钉钉回调过来的请求明细
  36. logger.Info(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj))
  37. // TODO: 校验请求
  38. if len(msgObj.Text.Content) == 1 || strings.TrimSpace(msgObj.Text.Content) == "帮助" {
  39. // 欢迎信息
  40. _, err := msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), Welcome)
  41. if err != nil {
  42. logger.Warning(fmt.Errorf("send message error: %v", err))
  43. return ship.ErrBadRequest.New(fmt.Errorf("send message error: %v", err))
  44. }
  45. } else {
  46. // 除去帮助之外的逻辑分流在这里处理
  47. switch {
  48. case strings.HasPrefix(strings.TrimSpace(msgObj.Text.Content), "#图片"):
  49. return process.ImageGenerate(&msgObj)
  50. default:
  51. msgObj.Text.Content, err = process.GeneratePrompt(strings.TrimSpace(msgObj.Text.Content))
  52. // err不为空:提示词之后没有文本 -> 直接返回提示词所代表的内容
  53. if err != nil {
  54. _, err = msgObj.ReplyToDingtalk(string(dingbot.TEXT), msgObj.Text.Content)
  55. if err != nil {
  56. logger.Warning(fmt.Errorf("send message error: %v", err))
  57. return err
  58. }
  59. return nil
  60. }
  61. logger.Info(fmt.Sprintf("after generate prompt: %#v", msgObj.Text.Content))
  62. return process.ProcessRequest(&msgObj)
  63. }
  64. }
  65. return nil
  66. })
  67. // 解析生成后的图片
  68. app.Route("/images/:filename").GET(func(c *ship.Context) error {
  69. filename := c.Param("filename")
  70. root := "./images/"
  71. return c.File(filepath.Join(root, filename))
  72. })
  73. port := ":" + public.Config.Port
  74. srv := &http.Server{
  75. Addr: port,
  76. Handler: app,
  77. }
  78. // Initializing the server in a goroutine so that
  79. // it won't block the graceful shutdown handling below
  80. go func() {
  81. logger.Info("🚀 The HTTP Server is running on", port)
  82. if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
  83. logger.Fatal("listen: %s\n", err)
  84. }
  85. }()
  86. // Wait for interrupt signal to gracefully shutdown the server with
  87. // a timeout of 5 seconds.
  88. quit := make(chan os.Signal, 1)
  89. // kill (no param) default send syscall.SIGTERM
  90. // kill -2 is syscall.SIGINT
  91. // kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
  92. // signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
  93. signal.Notify(quit, os.Interrupt)
  94. <-quit
  95. logger.Info("Shutting down server...")
  96. // 5秒后强制退出
  97. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  98. defer cancel()
  99. if err := srv.Shutdown(ctx); err != nil {
  100. logger.Fatal("Server forced to shutdown:", err)
  101. }
  102. logger.Info("Server exiting!")
  103. }
  104. var Welcome string = `# 发送信息
  105. 若您想给机器人发送信息,请选择:
  106. 1. 在本机器人所在群里@机器人;
  107. 2. 点击机器人的头像后,再点击"发消息"。
  108. 机器人收到您的信息后,默认会交给chatgpt进行处理。除非,您发送的内容是7个**系统指令**之一。
  109. -----
  110. # 系统指令
  111. 系统指令是一些特殊的词语,当您向机器人发送这些词语时,会触发对应的功能:
  112. **单聊**:每条消息都是单独的对话,不包含上下文
  113. **串聊**:对话会携带上下文,除非您主动重置对话或对话长度超过限制
  114. **重置**:重置上下文
  115. **余额**:查询机器人所用OpenAI账号的余额
  116. **模板**:查询机器人内置的快捷模板
  117. **图片**:查看如何根据提示词生成图片
  118. **帮助**:重新获取帮助信息
  119. -----
  120. # 友情提示
  121. 使用"串聊模式"会显著加快机器人所用账号的余额消耗速度。
  122. 因此,若无保留上下文的需求,建议使用"单聊模式"。
  123. 即使有保留上下文的需求,也应适时使用"重置"指令来重置上下文。
  124. -----
  125. # 项目地址
  126. 本项目已在GitHub开源,[查看源代码](https://github.com/eryajf/chatgpt-dingtalk)。
  127. `