main.go (3256B)
1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "github.com/go-mqtt/mqtt" 8 "github.com/pelletier/go-toml/v2" 9 "github.com/thoj/go-ircevent" 10 "log" 11 "os" 12 "strings" 13 "time" 14 ) 15 16 type IrcConfig struct { 17 Channel string 18 Server string 19 Nick string 20 CmdStart string 21 CmdMid string 22 CmdEnd string 23 MaxLine int 24 ContSuffix string 25 ContPrefix string 26 Verbose bool 27 } 28 29 type MqttConfig struct { 30 Server string 31 UserName string 32 Password string 33 } 34 35 type Config struct { 36 Irc IrcConfig 37 Mqtt MqttConfig 38 } 39 40 type Msg struct { 41 Topic []byte 42 Message []byte 43 } 44 45 func readConfig(path string, config *Config) (err error) { 46 var f *os.File 47 f, err = os.Open("mqttim.toml") 48 if err != nil { 49 log.Fatal(err) 50 return err 51 } 52 defer f.Close() 53 54 d := toml.NewDecoder(f) 55 err = d.Decode(config) 56 if err != nil { 57 log.Fatal(err) 58 } 59 60 return err 61 } 62 63 func main() { 64 var err error 65 var config Config 66 var m *mqtt.Client 67 68 ircQueue := make(chan Msg) 69 70 err = readConfig("mqttim.toml", &config) 71 if err != nil { 72 return 73 } 74 75 m, err = mqtt.VolatileSession("mqttim", &mqtt.Config{ 76 Dialer: mqtt.NewDialer("tcp", config.Mqtt.Server), 77 PauseTimeout: 4 * time.Second, 78 UserName: config.Mqtt.UserName, 79 Password: []byte(config.Mqtt.Password), 80 }) 81 if err != nil { 82 log.Fatal(err) 83 return 84 } 85 go m.Subscribe(nil, "#") 86 87 i := irc.IRC(config.Irc.Nick, "mqttim") 88 if config.Irc.Verbose { 89 i.VerboseCallbackHandler = true 90 i.Debug = true 91 } 92 i.AddCallback("001", func(e *irc.Event) { i.Join(config.Irc.Channel) }) 93 i.AddCallback("366", func(e *irc.Event) {}) 94 i.AddCallback("PRIVMSG", func(e *irc.Event) { 95 msg := e.Message() 96 if !strings.HasPrefix(msg, config.Irc.CmdStart) || !strings.HasSuffix(msg, config.Irc.CmdEnd) { 97 return 98 } 99 topic, payload, found := strings.Cut(msg, config.Irc.CmdMid) 100 if found { 101 go m.Publish(nil, []byte(payload), topic) 102 } 103 }) 104 err = i.Connect(config.Irc.Server) 105 if err != nil { 106 fmt.Printf("Err %s", err) 107 return 108 } 109 go mqtt2irc(m, ircQueue, &config) 110 go ircSender(&config.Irc, i, ircQueue) 111 i.Loop() 112 } 113 114 func dup(src []byte) []byte { 115 res := make([]byte, len(src)) 116 copy(res, src) 117 return res 118 } 119 120 func mqtt2irc(m *mqtt.Client, c chan Msg, config *Config) error { 121 var big *mqtt.BigMessage 122 123 for { 124 message, topic, err := m.ReadSlices() 125 switch { 126 case err == nil: 127 c <- Msg{Topic: dup(topic), Message: dup(message)} 128 case errors.As(err, &big): 129 c <- Msg{Topic: dup(topic), Message: []byte("<Big Message>")} 130 default: 131 log.Print(err) 132 return err 133 } 134 } 135 } 136 137 func ircSender(config *IrcConfig, i *irc.Connection, c chan Msg) error { 138 var buf bytes.Buffer 139 140 for { 141 m := <-c 142 143 if len(m.Topic)+2+len(m.Message) < config.MaxLine { 144 i.Privmsgf(config.Channel, "%s: %s", m.Topic, m.Message) 145 } else { 146 for s := 0; s < len(m.Message); { 147 l := len(m.Message) - s 148 buf.Reset() 149 buf.Write(m.Topic) 150 buf.WriteString(": ") 151 if s > 0 { 152 buf.WriteString(config.ContPrefix) 153 } 154 if buf.Len()+l <= config.MaxLine { 155 buf.Write(m.Message[s:]) 156 } else { 157 l = config.MaxLine - buf.Len() - len(config.ContSuffix) 158 buf.Write(m.Message[s : s+l]) 159 buf.WriteString(config.ContSuffix) 160 } 161 i.Privmsg(config.Channel, string(buf.Bytes())) 162 s += l 163 } 164 } 165 } 166 }