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.

180 lines
3.9 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) addChannel(channelName string) *Channel {
  65. if bot.channels[channelName] != nil {
  66. return bot.channels[channelName]
  67. }
  68. channel := newChannel(bot, channelName, bot.client)
  69. go channel.loop()
  70. bot.channels[channelName] = channel
  71. return channel
  72. }
  73. func (bot *Bot) handlePost(channelName string, post ChannelPost) {
  74. channel := bot.channels[channelName]
  75. if channel == nil {
  76. channel = bot.addChannel(channelName)
  77. }
  78. channel.ch <- post
  79. }
  80. func (bot *Bot) runCommands(commands []string, target irc.Target, replacers map[string]string) {
  81. if replacers == nil {
  82. replacers = make(map[string]string)
  83. }
  84. replacers["me"] = bot.client.Nick()
  85. for _, command := range commands {
  86. for from, to := range replacers {
  87. command = strings.Replace(command, "%"+from, to, -1)
  88. }
  89. bot.client.EmitInput(command, target)
  90. }
  91. }
  92. func (bot *Bot) loop() {
  93. bot.loopCtx, bot.loopCancel = context.WithCancel(bot.ctx)
  94. channelChanges, err := channels.SubscribeLogged(bot.ctx)
  95. if err != nil {
  96. log.Println("Failed to get channel changes:", err)
  97. return
  98. }
  99. for {
  100. select {
  101. case channel := <-channelChanges:
  102. {
  103. channels := []models.Channel{channel}
  104. deadline := time.After(time.Second * 1)
  105. buffering := true
  106. for buffering {
  107. select {
  108. case channel := <-channelChanges:
  109. channels = append(channels, channel)
  110. case <-deadline:
  111. buffering = false
  112. }
  113. }
  114. decisions := make(map[string]bool)
  115. for _, channel := range channels {
  116. decisions[channel.Name] = channel.Logged
  117. }
  118. joins := make([]string, 0, len(decisions))
  119. parts := make([]string, 0, len(decisions))
  120. for channelName, logged := range decisions {
  121. if logged {
  122. if bot.client.Channel(channelName) != nil {
  123. continue
  124. }
  125. joins = append(joins, channelName)
  126. } else {
  127. if bot.client.Channel(channelName) == nil {
  128. continue
  129. }
  130. parts = append(parts, channelName)
  131. }
  132. }
  133. if len(joins) > 0 {
  134. bot.client.Join(joins...)
  135. }
  136. if len(parts) > 0 {
  137. bot.client.Part(parts...)
  138. }
  139. }
  140. case <-bot.loopCtx.Done():
  141. {
  142. log.Println("Spinning down bot loop.")
  143. return
  144. }
  145. }
  146. }
  147. }
  148. func (bot *Bot) stopLoop() {
  149. if bot.loopCancel != nil {
  150. bot.loopCancel()
  151. }
  152. }