Blob
Date:
Thu Dec 2 14:24:51 2021
UTC
Message:
fetch links title and send them back as privmsg
just like the playonbsd bot :)
package mainimport ("crypto/tls""flag""fmt""io""io/ioutil""log""net/http""path""path/filepath""regexp""strings"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]+`))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 getPageTitle(node *html.Node) string {for child := node.FirstChild; child != nil; child = child.NextSibling {if child.Type == html.ElementNode && child.Data == "title" {text := node.FirstChild.Datareturn strings.Trim(text, " \t\n")}}return ""}func getPageHead(node *html.Node) *html.Node {if node.Type == html.ElementNode && node.Data == "head" {return node}for child := node.FirstChild; child != nil; child = child.NextSibling {if n := getPageHead(node); n != nil {return n}}return nil}func pagetitle(conn *irc.Conn, line *irc.Line) {matches := httplink.FindAllString(line.Text(), -1)for _, link := range matches {resp, err := http.Get(link)if err != nil {continue}defer resp.Body.Close()doc, err := html.Parse(resp.Body)if err != nil {continue}head := getPageHead(doc)if head == nil {continue}if title := getPageTitle(head); title != "" {conn.Privmsg(channel, title)}}}func dostuff(conn *irc.Conn, line *irc.Line) {matrix2gemini(conn, line)messageTooLong(conn, line)pagetitle(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