package main import ( "crypto/tls" "log" "net" "net/http" "os" "fmt" "encoding/json" "io/ioutil" "strings" "html" "regexp" ) type Blog struct { Id string `json:"id"` Content string `json:"content"` Date string `json:"created_at"` } type Config struct { Listen string `json:"listen"` CertPath string `json:"cert_path"` KeyPath string `json:"key_path"` BaseURL string `json:"base_url"` } func main() { config := getConfig() listener := listen(config.Listen, config.CertPath, config.KeyPath) log.Println("Server successfully started") log.Println("Server is listening at " + config.Listen) serve(listener, config.BaseURL) } func getConfig() Config { configPath := os.Getenv("MASTOGEM_CONFIG_PATH") if configPath == "" { log.Println("MASTOGEM_CONFIG_PATH was not set, using default settings") config := Config{ Listen: "127.0.0.1:1965", CertPath: "cert.pem", KeyPath: "key.rsa", BaseURL: "https://mamot.fr", } return config } configFile, err := ioutil.ReadFile(configPath) if err != nil { log.Fatalln("config file: %s", err) } var config Config json.Unmarshal(configFile, &config) return config } func listen(address, certFile, keyFile string) net.Listener { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { log.Fatalln("loadkeys: %s", err) } config := &tls.Config{ ClientAuth: tls.RequestClientCert, Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS12, InsecureSkipVerify: true, } listener, err := tls.Listen("tcp", address, config) if err != nil { log.Fatalln("failed to listen on 0.0.0.0:1965: %s", err) } return listener } func serve(listener net.Listener, baseURL string) { for { conn, err := listener.Accept() if err != nil { log.Println(err) } go handleConn(conn.(*tls.Conn), baseURL) } } func handleConn(conn *tls.Conn, baseURL string) { defer conn.Close() blogs := getBlog(baseURL, "138624") _, err := fmt.Fprintf(conn, "20 text/gemini\r\n# Picasoft account toots\n") if err != nil { log.Println("handleConn: %s", err) return } for _, blog := range blogs { date := "```\n* Posted at " + blog.Date + "\n```\n" text := blog.Content + "\n" text = strings.ReplaceAll(text, "

", "") text = strings.ReplaceAll(text, "

", "\n\n") text = strings.ReplaceAll(text, "
", "\n") text = strings.ReplaceAll(text, "", "") text = strings.ReplaceAll(text, "", "") regexString := "]*)?>" regex, err := regexp.Compile(regexString) if err != nil { log.Println("regex: %s", err) return } text = regex.ReplaceAllLiteralString(text, "") regexString = "]*)?>" regex, err = regexp.Compile(regexString) if err != nil { log.Println("regex: %s", err) return } text = regex.ReplaceAllLiteralString(text, "") text = html.UnescapeString(text) _, err = fmt.Fprintf(conn, date + text) if err != nil { log.Println("read blogs: %s", err) return } } } func getBlog(baseURL, account string) []Blog { if baseURL == "" || account == "" { log.Println("baseURL or account is empty") return nil } resp, err := http.Get(baseURL + "/api/v1/accounts/" + account + "/statuses?exclude_reblogs=true&exlude_replies=true") if err != nil { log.Println("Mastodon API request: %s", err) return nil } defer resp.Body.Close() if resp.StatusCode != 200 { log.Println("Mastodon API response: %s", resp.Status) return nil } body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Println("Mastodon response body: %s", err) } var blogs []Blog json.Unmarshal(body, &blogs) return blogs }