Blob
Date:
Sat Jan 8 15:47:57 2022
UTC
Message:
fetch the title also for gemini URLs
package mainimport ("crypto/tls""flag""fmt""io""io/ioutil""log""net/http""path""path/filepath""regexp""strings""github.com/andybalholm/cascadia"irc "github.com/fluffle/goirc/client""golang.org/x/net/html")var (baseurl = flag.String("baseurl", "gemini://m2i.omarpolo.com", "base url")matrixOutDir = flag.String("matrix-out", "", "matrix out directory")msgRe = regexp.MustCompile(`https://.*/[^\s]+\.(txt|png|jpg|jpeg|gif)`)channel = "#gemini-it"tooLongRe = regexp.MustCompile(`full message at (https://libera.ems.host/.*)[)]`)httplink = regexp.MustCompile(`https?://[^\s)]+`)gemlink = regexp.MustCompile(`gemini://[^\s)]+`))func matrix2gemini(conn *irc.Conn, line *irc.Line) {matches := msgRe.FindAllString(line.Text(), -1)// it's not a good idea to defer inside a loop, but we know// len(matches) is small (usually just 1). Morover, I like// living in danger!for _, link := range matches {resp, err := http.Get(link)if err != nil {conn.Privmsg(channel,fmt.Sprintf("failed to download %q: %s", link, err),)continue}defer resp.Body.Close()ext := path.Ext(link)tmpfile, err := ioutil.TempFile(*matrixOutDir, "message-*"+ext)if err != nil {conn.Privmsg(channel, fmt.Sprintf("failed to tmpfile: %s", err))return}defer tmpfile.Close()io.Copy(tmpfile, resp.Body)conn.Privmsg(channel,fmt.Sprintf("better: %s/%s",*baseurl,filepath.Base(tmpfile.Name()),),)}}func messageTooLong(conn *irc.Conn, line *irc.Line) {matches := tooLongRe.FindStringSubmatch(line.Text())if len(matches) != 2 {return}url := matches[1]resp, err := http.Get(url)if err != nil {conn.Privmsg(channel,fmt.Sprintf("failed to download %q: %s", url, err),)return}defer resp.Body.Close()sb := &strings.Builder{}if _, err := io.Copy(sb, resp.Body); err != nil {conn.Privmsg(channel,fmt.Sprintf("failed to read body of %q: %s", url, err),)return}conn.Privmsg(channel, fmt.Sprintf("%s ha detto:", line.Nick))for _, line := range strings.Split(sb.String(), "\n") {conn.Privmsg(channel,line,)}}func stringifyNode(node *html.Node) string {s := ""if node.Type == html.TextNode {return node.Data}for child := node.FirstChild; child != nil; child = child.NextSibling {s += stringifyNode(child)}return s}func wwwpagetitle(conn *irc.Conn, line *irc.Line) {log.Println("text is", line.Text())matches := httplink.FindAllString(line.Text(), -1)for _, link := range matches {log.Println("fetching", link, "...")resp, err := http.Get(link)if err != nil {continue}defer resp.Body.Close()doc, err := html.Parse(resp.Body)if err != nil {continue}sel := cascadia.MustCompile("head > title")n := cascadia.Query(doc, sel)if n == nil {continue}title := stringifyNode(n)if len(title) > 50 {title = title[:50]}conn.Privmsg(channel, stringifyNode(n))}}func gempagetitle(conn *irc.Conn, line *irc.Line) {matches := gemlink.FindAllString(line.Text(), -1)for _, link := range matches {log.Println("fetching", link, "...")title, err := geminiTitle(link)if err != nil {continue}conn.Privmsg(channel, title)}}func dostuff(conn *irc.Conn, line *irc.Line) {matrix2gemini(conn, line)messageTooLong(conn, line)wwwpagetitle(conn, line)gempagetitle(conn, line)// ...}func main() {flag.Parse()cfg := irc.NewConfig("gemitbot")cfg.SSL = truecfg.SSLConfig = &tls.Config{ServerName: "irc.libera.chat"}cfg.Server = "irc.libera.chat:7000"cfg.NewNick = func(n string) string { return n + "^" }c := irc.Client(cfg)c.HandleFunc(irc.CONNECTED, func(conn *irc.Conn, line *irc.Line) {log.Println("connected, joining", channel)conn.Join(channel)})c.HandleFunc(irc.PRIVMSG, dostuff)c.HandleFunc(irc.ACTION, dostuff)quit := make(chan bool)c.HandleFunc(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) {quit <- true})if err := c.Connect(); err != nil {log.Fatalln("connection error:", err)}<-quit}
Omar Polo