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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package main
  2. import (
  3. "bytes"
  4. "crypto/tls"
  5. "fmt"
  6. "github.com/irccloud/irccat/httplistener"
  7. "github.com/irccloud/irccat/tcplistener"
  8. "github.com/juju/loggo"
  9. "github.com/spf13/viper"
  10. "github.com/thoj/go-ircevent"
  11. "os"
  12. "os/exec"
  13. "os/signal"
  14. "strings"
  15. "syscall"
  16. )
  17. var log = loggo.GetLogger("main")
  18. type IRCCat struct {
  19. control_chan string
  20. irc *irc.Connection
  21. tcp *tcplistener.TCPListener
  22. signals chan os.Signal
  23. }
  24. func main() {
  25. loggo.ConfigureLoggers("<root>=DEBUG")
  26. log.Infof("IRCCat starting...")
  27. viper.SetConfigName("irccat")
  28. viper.AddConfigPath("/etc")
  29. viper.AddConfigPath(".")
  30. var err error
  31. err = viper.ReadInConfig()
  32. if err != nil {
  33. log.Errorf("Error reading config file - exiting. I'm looking for irccat.[json|yaml|toml|hcl] in . or /etc")
  34. return
  35. }
  36. irccat := IRCCat{}
  37. irccat.signals = make(chan os.Signal, 1)
  38. signal.Notify(irccat.signals, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
  39. go irccat.signalHandler()
  40. irccat.tcp, err = tcplistener.New()
  41. if err != nil {
  42. return
  43. }
  44. err = irccat.connectIRC()
  45. if err != nil {
  46. log.Criticalf("Error connecting to IRC server: %s", err)
  47. return
  48. }
  49. httplistener.New(irccat.irc)
  50. irccat.tcp.Run(irccat.irc)
  51. irccat.irc.Loop()
  52. }
  53. func (i *IRCCat) signalHandler() {
  54. sig := <-i.signals
  55. log.Infof("Exiting on %s", sig)
  56. i.irc.QuitMessage = fmt.Sprintf("Exiting on %s", sig)
  57. i.irc.Quit()
  58. }
  59. func (i *IRCCat) connectIRC() error {
  60. irccon := irc.IRC(viper.GetString("irc.nick"), viper.GetString("irc.realname"))
  61. irccon.Debug = true
  62. irccon.UseTLS = viper.GetBool("irc.tls")
  63. if viper.GetBool("irc.tls_skip_verify") {
  64. irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true, MaxVersion: tls.VersionTLS11}
  65. }
  66. err := irccon.Connect(viper.GetString("irc.server"))
  67. if err != nil {
  68. return err
  69. }
  70. irccon.AddCallback("001", i.handleWelcome)
  71. irccon.AddCallback("PRIVMSG", func(event *irc.Event) {
  72. if event.Message()[0] == '?' || event.Message()[0] == '!' {
  73. go i.handleCommand(event)
  74. }
  75. })
  76. i.irc = irccon
  77. return nil
  78. }
  79. func (i *IRCCat) authorisedUser(nick string) bool {
  80. return false
  81. }
  82. func (i *IRCCat) handleWelcome(e *irc.Event) {
  83. for _, channel := range viper.GetStringSlice("irc.channels") {
  84. i.irc.Join(channel)
  85. }
  86. }
  87. func (i *IRCCat) handleCommand(event *irc.Event) {
  88. msg := event.Message()
  89. respond_to := event.Arguments[0]
  90. if respond_to[0] != '#' && !i.authorisedUser(event.Nick) {
  91. // Command not in a channel, or not from an authorised user
  92. log.Infof("Unauthorised command: %s (%s) %s", event.Nick, respond_to, msg)
  93. return
  94. }
  95. log.Infof("Authorised command: %s (%s) %s", event.Nick, respond_to, msg)
  96. channel := ""
  97. if respond_to[0] == '#' {
  98. channel = respond_to
  99. }
  100. parts := strings.SplitN(msg, " ", 1)
  101. var cmd *exec.Cmd
  102. if len(parts) == 1 {
  103. cmd = exec.Command(viper.GetString("commands.handler"), event.Nick, channel, respond_to, parts[0][1:])
  104. } else {
  105. cmd = exec.Command(viper.GetString("commands.handler"), event.Nick, channel, respond_to, parts[0][1:], parts[1])
  106. }
  107. i.runCommand(cmd, respond_to)
  108. }
  109. // Run a command with the output going to the nick/channel identified by respond_to
  110. func (i *IRCCat) runCommand(cmd *exec.Cmd, respond_to string) {
  111. var out bytes.Buffer
  112. cmd.Stdout = &out
  113. cmd.Stderr = &out
  114. err := cmd.Run()
  115. if err != nil {
  116. log.Errorf("Running command %s failed: %s", cmd.Args, err)
  117. i.irc.Privmsgf(respond_to, "Command failed: %s", err)
  118. }
  119. lines := strings.Split(out.String(), "\n")
  120. line_count := len(lines)
  121. if line_count > viper.GetInt("commands.max_response_lines") {
  122. line_count = viper.GetInt("commands.max_response_lines")
  123. }
  124. for _, line := range lines[0:line_count] {
  125. if line != "" {
  126. i.irc.Privmsg(respond_to, line)
  127. }
  128. }
  129. }