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.

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