Skip to content

Instantly share code, notes, and snippets.

@toshiharu
Last active January 6, 2023 13:29
Show Gist options
  • Select an option

  • Save toshiharu/362eb3b679a3747097f322ba11f2b2b3 to your computer and use it in GitHub Desktop.

Select an option

Save toshiharu/362eb3b679a3747097f322ba11f2b2b3 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"log"
"flag"
"os"
yaml "gopkg.in/yaml.v2"
"path/filepath"
"os/user"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"github.com/mmcdole/gofeed"
"time"
"reflect" // https://stackoverflow.com/questions/22367337/last-item-in-a-template-range
"text/template"
"bytes"
"net/smtp"
"github.com/domodwyer/mailyak"
)
var Logger *log.Logger
type Config struct {
MailTo string `yaml:"mailto"`
MailFrom string `yaml:"mailfrom"`
UserName string `yaml:"username"`
Password string `yaml:"password"`
Feed []string `yaml:"feed"`
}
const tmplStrFmt = `
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
<body>
{{ range $i, $e := . -}}
<div>
<div>
<strong style="font-weight:bold;font-size:1.2em">{{ $e.Title }}</strong><br />
Link: <a href="{{ $e.Link }}">{{ $e.Link }}</a><br />
</div>
{{ if $e.Content }}<div style="padding: 1em 0">{{ $e.Content }}</div>{{ else }}{{ if $e.Description }}<div style="padding: 1em 0">{{ $e.Description }}</div>{{ else }}<br />{{ end }}{{ end }}
</div>
<div style="font-size:0.8em">Posted on {{ if $e.PublishedParsed }}{{ toRFC1123Z $e.PublishedParsed }}{{ else }}{{ if $e.UpdatedParsed }}{{ toRFC1123Z $e.UpdatedParsed }}{{ else }}{{ epochRFC1123Z }}{{ end }}{{ end }} | <a href="{{ $e.Link }}">permalink</a> | <a href="%s">%s</a><br clear="all" /></div>
</div>
{{ if last $i $ }}{{ else }}<hr />{{ end }}
{{- end }}
</body>
</html>
`
var funcMap = template.FuncMap{
"toRFC1123Z": func(t time.Time) string { return t.Format(time.RFC1123Z) },
"epochRFC1123Z": func() string { return time.Unix(0, 0).Format(time.RFC1123Z) },
"last": func(x int, a interface{}) bool { return x == reflect.ValueOf(a).Len() - 1 },
}
func getDateTime(item *gofeed.Item) (dc int64) {
if item.PublishedParsed != nil {
dc = item.PublishedParsed.Unix()
} else if item.UpdatedParsed != nil {
dc = item.UpdatedParsed.Unix()
} else {
dc = 0
}
return
}
func main() {
Logger := log.New(os.Stdout, "ykr ", log.LstdFlags)
configPath := flag.String("c", "", "a config file")
flag.Parse()
f, err := os.Open(*configPath)
if err != nil {
panic(err)
}
defer f.Close()
decoder := yaml.NewDecoder(f)
var config Config
if err := decoder.Decode(&config); err != nil {
panic(err)
}
basename := filepath.Base(*configPath)
extname := filepath.Ext(*configPath)
name := basename[:len(basename)-len(extname)]
usr, _ := user.Current()
homeDir := usr.HomeDir
appDir := filepath.Join(homeDir, ".ykr")
if _, err := os.Stat(appDir); os.IsNotExist(err) {
err := os.MkdirAll(appDir, 0755)
if err != nil {
panic(err)
}
}
dbPath := filepath.Join(appDir, name + ".sqlite3")
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
panic(err)
}
defer db.Close()
createQuery := `CREATE TABLE IF NOT EXISTS item(
id INTEGER PRIMARY KEY,
feedurl TEXT NOT NULL,
link TEXT NOT NULL,
datetime INTEGER)`
db.Exec(createQuery)
fp := gofeed.NewParser()
for _, url := range config.Feed {
feed, err := fp.ParseURL(url)
if err != nil {
Logger.Println(url)
Logger.Print(err)
continue
}
newItems := []*gofeed.Item{}
for _, item := range feed.Items {
dc := getDateTime(item)
row := db.QueryRow(`SELECT feedurl, link, datetime FROM item WHERE feedurl = ? AND link = ? AND datetime = ?`, url, item.Link, dc)
var (
feedUrl string
link string
dateTime int64
)
err := row.Scan(&feedUrl, &link, &dateTime)
if err != nil {
newItems = append(newItems, item)
}
}
if len(newItems) == 0 {
continue
}
tmplStr := fmt.Sprintf(tmplStrFmt, feed.Link, feed.Title)
tmpl, _ := template.New("").Funcs(funcMap).Parse(tmplStr)
var buf bytes.Buffer
tmpl.Execute(&buf, newItems)
auth := smtp.PlainAuth("", config.UserName, config.Password, "smtp.gmail.com")
mail := mailyak.New("smtp.gmail.com:587", auth)
mail.To(config.MailTo)
mail.From(config.MailFrom)
mail.FromName(feed.Title)
mail.Subject(feed.Title)
mail.HTML().Set(buf.String())
if err := mail.Send(); err != nil {
panic(err)
}
for _, item := range newItems {
dc := getDateTime(item)
_, err = db.Exec("INSERT INTO item (feedurl, link, datetime) VALUES (?, ?, ?)", url, item.Link, dc)
if err != nil {
panic(err)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment