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.

178 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
  1. package bot
  2. import (
  3. "context"
  4. "log"
  5. "strings"
  6. "time"
  7. "git.aiterp.net/gisle/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. bot := &Bot{
  32. client: client,
  33. channels: make(map[string]*Channel),
  34. }
  35. client.SetValue(botKey, bot)
  36. return bot
  37. }
  38. // Connect connects the bot to the IRC server. This will disconnect already
  39. // established connections.
  40. func (bot *Bot) Connect(server string, ssl bool, maxRetries int) (err error) {
  41. if bot.ctxCancel != nil {
  42. bot.ctxCancel()
  43. }
  44. bot.ctx, bot.ctxCancel = context.WithCancel(bot.client.Context())
  45. retries := 0
  46. for maxRetries == 0 || retries < maxRetries {
  47. err = bot.client.Connect(server, ssl)
  48. if err != nil {
  49. log.Println("Connect failed:", err.Error())
  50. if maxRetries > 0 && retries < maxRetries {
  51. retries++
  52. log.Printf("Retrying in 5s (Retry %d/%d)", retries, maxRetries)
  53. } else {
  54. log.Println("Retrying in 5s (No retry limit)")
  55. }
  56. time.Sleep(time.Second * 10)
  57. continue
  58. }
  59. return nil
  60. }
  61. return err
  62. }
  63. func (bot *Bot) addChannel(channelName string) *Channel {
  64. if bot.channels[channelName] != nil {
  65. return bot.channels[channelName]
  66. }
  67. channel := newChannel(bot, channelName, bot.client)
  68. go channel.loop()
  69. bot.channels[channelName] = channel
  70. return channel
  71. }
  72. func (bot *Bot) handlePost(channelName string, post ChannelPost) {
  73. channel := bot.channels[channelName]
  74. if channel == nil {
  75. channel = bot.addChannel(channelName)
  76. }
  77. channel.ch <- post
  78. }
  79. func (bot *Bot) runCommands(commands []string, target irc.Target, replacers map[string]string) {
  80. if replacers == nil {
  81. replacers = make(map[string]string)
  82. }
  83. replacers["me"] = bot.client.Nick()
  84. for _, command := range commands {
  85. for from, to := range replacers {
  86. command = strings.Replace(command, "%"+from, to, -1)
  87. }
  88. bot.client.EmitInput(command, target)
  89. }
  90. }
  91. func (bot *Bot) loop() {
  92. bot.loopCtx, bot.loopCancel = context.WithCancel(bot.ctx)
  93. channelChanges, err := channels.SubscribeLogged(bot.ctx)
  94. if err != nil {
  95. log.Println("Failed to get channel changes:", err)
  96. return
  97. }
  98. for {
  99. select {
  100. case channel := <-channelChanges:
  101. {
  102. channels := []models.Channel{channel}
  103. deadline := time.After(time.Second * 1)
  104. buffering := true
  105. for buffering {
  106. select {
  107. case channel := <-channelChanges:
  108. channels = append(channels, channel)
  109. case <-deadline:
  110. buffering = false
  111. }
  112. }
  113. decisions := make(map[string]bool)
  114. for _, channel := range channels {
  115. decisions[channel.Name] = channel.Logged
  116. }
  117. joins := make([]string, 0, len(decisions))
  118. parts := make([]string, 0, len(decisions))
  119. for channelName, logged := range decisions {
  120. if logged {
  121. if bot.client.Channel(channelName) != nil {
  122. continue
  123. }
  124. joins = append(joins, channelName)
  125. } else {
  126. if bot.client.Channel(channelName) == nil {
  127. continue
  128. }
  129. parts = append(parts, channelName)
  130. }
  131. }
  132. if len(joins) > 0 {
  133. bot.client.Join(joins...)
  134. }
  135. if len(parts) > 0 {
  136. bot.client.Part(parts...)
  137. }
  138. }
  139. case <-bot.loopCtx.Done():
  140. {
  141. log.Println("Spinning down bot loop.")
  142. return
  143. }
  144. }
  145. }
  146. }
  147. func (bot *Bot) stopLoop() {
  148. if bot.loopCancel != nil {
  149. bot.loopCancel()
  150. }
  151. }