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.

179 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. "git.aiterp.net/rpdata/logbot3/internal/models/channels"
  8. "github.com/gissleh/irc"
  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. client.AddHandler(handler)
  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. bot.channels = make(map[string]*Channel)
  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. ticker := time.NewTicker(time.Second * 10)
  98. defer ticker.Stop()
  99. for {
  100. select {
  101. case <-ticker.C:
  102. {
  103. loggedChannels, err := channels.ListLogged(bot.ctx)
  104. if err != nil {
  105. log.Println("Failed to list logged channels:", err)
  106. continue
  107. }
  108. loggedMap := make(map[string]bool)
  109. for _, channel := range loggedChannels {
  110. loggedMap[channel.Name] = true
  111. }
  112. clientMap := make(map[string]bool)
  113. for _, channel := range bot.client.Channels() {
  114. clientMap[channel.Name()] = true
  115. }
  116. joins := make([]string, 0, len(loggedMap))
  117. parts := make([]string, 0, len(clientMap))
  118. for key := range loggedMap {
  119. if !clientMap[key] {
  120. joins = append(joins, key)
  121. }
  122. }
  123. for key := range clientMap {
  124. if !loggedMap[key] {
  125. parts = append(parts, key)
  126. }
  127. }
  128. if len(parts) > 0 {
  129. log.Println("Leaving", strings.Join(parts, ", "))
  130. bot.client.Part(parts...)
  131. }
  132. if len(joins) > 0 {
  133. log.Println("Joining", strings.Join(joins, ", "))
  134. bot.client.Join(joins...)
  135. }
  136. }
  137. case <-bot.loopCtx.Done():
  138. {
  139. log.Println("Spinning down bot loop.")
  140. return
  141. }
  142. }
  143. }
  144. }
  145. func (bot *Bot) stopLoop() {
  146. if bot.loopCancel != nil {
  147. bot.loopCancel()
  148. }
  149. }