The new logbot, not committed from the wrong terminal window this time.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
4.1 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. package bot
  2. import (
  3. "context"
  4. "log"
  5. "strings"
  6. "time"
  7. "github.com/gissleh/irc"
  8. "git.aiterp.net/rpdata/logbot3/internal/models"
  9. "git.aiterp.net/rpdata/logbot3/internal/models/channels"
  10. )
  11. var botKey = "git.aiterp.net/rpdata/logbot.Bot.key"
  12. // The Bot is the IRC client.
  13. type Bot struct {
  14. client *irc.Client
  15. ctx context.Context
  16. ctxCancel context.CancelFunc
  17. loopCtx context.Context
  18. loopCancel context.CancelFunc
  19. channels map[string]*Channel
  20. }
  21. // New creates a new Bot.
  22. func New(ctx context.Context, nick string, alternatives []string, user string, realName string) *Bot {
  23. client := irc.New(ctx, irc.Config{
  24. Nick: nick,
  25. User: user,
  26. RealName: realName,
  27. Alternatives: alternatives,
  28. SendRate: 2,
  29. SkipSSLVerification: false,
  30. })
  31. client.AddHandler(handler)
  32. bot := &Bot{
  33. client: client,
  34. channels: make(map[string]*Channel),
  35. }
  36. client.SetValue(botKey, bot)
  37. return bot
  38. }
  39. // Connect connects the bot to the IRC server. This will disconnect already
  40. // established connections.
  41. func (bot *Bot) Connect(server string, ssl bool, maxRetries int) (err error) {
  42. if bot.ctxCancel != nil {
  43. bot.ctxCancel()
  44. }
  45. bot.ctx, bot.ctxCancel = context.WithCancel(bot.client.Context())
  46. retries := 0
  47. for maxRetries == 0 || retries < maxRetries {
  48. err = bot.client.Connect(server, ssl)
  49. if err != nil {
  50. log.Println("Connect failed:", err.Error())
  51. if maxRetries > 0 && retries < maxRetries {
  52. retries++
  53. log.Printf("Retrying in 5s (Retry %d/%d)", retries, maxRetries)
  54. } else {
  55. log.Println("Retrying in 5s (No retry limit)")
  56. }
  57. time.Sleep(time.Second * 10)
  58. continue
  59. }
  60. return nil
  61. }
  62. return err
  63. }
  64. func (bot *Bot) ClientState() irc.ClientState {
  65. return bot.client.State()
  66. }
  67. func (bot *Bot) addChannel(channelName string) *Channel {
  68. if bot.channels[channelName] != nil {
  69. return bot.channels[channelName]
  70. }
  71. channel := newChannel(bot, channelName, bot.client)
  72. go channel.loop()
  73. bot.channels[channelName] = channel
  74. return channel
  75. }
  76. func (bot *Bot) handlePost(channelName string, post ChannelPost) {
  77. channel := bot.channels[channelName]
  78. if channel == nil {
  79. channel = bot.addChannel(channelName)
  80. }
  81. channel.ch <- post
  82. }
  83. func (bot *Bot) runCommands(commands []string, target irc.Target, replacers map[string]string) {
  84. if replacers == nil {
  85. replacers = make(map[string]string)
  86. }
  87. replacers["me"] = bot.client.Nick()
  88. for _, command := range commands {
  89. for from, to := range replacers {
  90. command = strings.Replace(command, "%"+from, to, -1)
  91. }
  92. bot.client.EmitInput(command, target)
  93. }
  94. }
  95. func (bot *Bot) loop() {
  96. bot.loopCtx, bot.loopCancel = context.WithCancel(bot.ctx)
  97. channelChanges, err := channels.SubscribeLogged(bot.ctx)
  98. if err != nil {
  99. log.Println("Failed to get channel changes:", err)
  100. return
  101. }
  102. for {
  103. select {
  104. case channel := <-channelChanges:
  105. {
  106. channels := []models.Channel{channel}
  107. deadline := time.After(time.Second * 1)
  108. buffering := true
  109. for buffering {
  110. select {
  111. case channel := <-channelChanges:
  112. channels = append(channels, channel)
  113. case <-deadline:
  114. buffering = false
  115. }
  116. }
  117. decisions := make(map[string]bool)
  118. for _, channel := range channels {
  119. decisions[channel.Name] = channel.Logged
  120. }
  121. joins := make([]string, 0, len(decisions))
  122. parts := make([]string, 0, len(decisions))
  123. for channelName, logged := range decisions {
  124. if logged {
  125. if bot.client.Channel(channelName) != nil {
  126. continue
  127. }
  128. joins = append(joins, channelName)
  129. } else {
  130. if bot.client.Channel(channelName) == nil {
  131. continue
  132. }
  133. parts = append(parts, channelName)
  134. }
  135. }
  136. if len(parts) > 0 {
  137. log.Println("Leaving", strings.Join(parts, ", "))
  138. bot.client.Part(parts...)
  139. }
  140. if len(joins) > 0 {
  141. log.Println("Joining", strings.Join(joins, ", "))
  142. bot.client.Join(joins...)
  143. }
  144. }
  145. case <-bot.loopCtx.Done():
  146. {
  147. log.Println("Spinning down bot loop.")
  148. return
  149. }
  150. }
  151. }
  152. }
  153. func (bot *Bot) stopLoop() {
  154. if bot.loopCancel != nil {
  155. bot.loopCancel()
  156. }
  157. }