Blob


1 package main
3 import (
4 "crypto/tls"
5 "flag"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "log"
10 "net/http"
11 "path"
12 "path/filepath"
13 "regexp"
14 "strings"
16 irc "github.com/fluffle/goirc/client"
17 )
19 var (
20 baseurl = flag.String("baseurl", "gemini://m2i.omarpolo.com", "base url")
21 matrixOutDir = flag.String("matrix-out", "", "matrix out directory")
23 msgRe = regexp.MustCompile(`https://.*/[^\s]+\.(txt|png|jpg|jpeg|gif)`)
24 channel = "#gemini-it"
26 tooLongRe = regexp.MustCompile(`full message at (https://libera.ems.host/.*)[)]`)
27 )
29 func matrix2gemini(conn *irc.Conn, line *irc.Line) {
30 matches := msgRe.FindAllString(line.Text(), -1)
32 // it's not a good idea to defer inside a loop, but we know
33 // len(matches) is small (usually just 1). Morover, I like
34 // living in danger!
36 for _, link := range matches {
37 resp, err := http.Get(link)
38 if err != nil {
39 conn.Privmsg(
40 channel,
41 fmt.Sprintf("failed to download %q: %s", link, err),
42 )
43 continue
44 }
45 defer resp.Body.Close()
47 ext := path.Ext(link)
48 tmpfile, err := ioutil.TempFile(*matrixOutDir, "message-*"+ext)
49 if err != nil {
50 conn.Privmsg(channel, fmt.Sprintf("failed to tmpfile: %s", err))
51 return
52 }
53 defer tmpfile.Close()
55 io.Copy(tmpfile, resp.Body)
57 conn.Privmsg(
58 channel,
59 fmt.Sprintf(
60 "better: %s/%s",
61 *baseurl,
62 filepath.Base(tmpfile.Name()),
63 ),
64 )
65 }
66 }
68 func messageTooLong(conn *irc.Conn, line *irc.Line) {
69 matches := tooLongRe.FindStringSubmatch(line.Text())
70 if len(matches) != 2 {
71 return
72 }
74 url := matches[1]
76 resp, err := http.Get(url)
77 if err != nil {
78 conn.Privmsg(
79 channel,
80 fmt.Sprintf("failed to download %q: %s", url, err),
81 )
82 return
83 }
84 defer resp.Body.Close()
86 sb := &strings.Builder{}
87 if _, err := io.Copy(sb, resp.Body); err != nil {
88 conn.Privmsg(
89 channel,
90 fmt.Sprintf("failed to read body of %q: %s", url, err),
91 )
92 return
93 }
95 conn.Privmsg(channel, fmt.Sprintf("%s ha detto:", line.Nick))
96 for _, line := range strings.Split(sb.String(), "\n") {
97 conn.Privmsg(
98 channel,
99 line,
104 func dostuff(conn *irc.Conn, line *irc.Line) {
105 matrix2gemini(conn, line)
106 messageTooLong(conn, line)
107 // ...
110 func main() {
111 flag.Parse()
113 cfg := irc.NewConfig("gemitbot")
114 cfg.SSL = true
115 cfg.SSLConfig = &tls.Config{ServerName: "irc.libera.chat"}
116 cfg.Server = "irc.libera.chat:7000"
117 cfg.NewNick = func(n string) string { return n + "^" }
119 c := irc.Client(cfg)
121 c.HandleFunc(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) {
122 log.Println("connected, joining", channel)
123 conn.Join(channel)
124 })
126 c.HandleFunc(irc.PRIVMSG, dostuff)
127 c.HandleFunc(irc.ACTION, dostuff)
129 quit := make(chan bool)
131 c.HandleFunc(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) {
132 quit <- true
133 })
135 if err := c.Connect(); err != nil {
136 log.Fatalln("connection error:", err)
139 <-quit