func ExtractAndAggPapersFromMsgs(msgs []*gmail.Message, authors, refs bool) (*Stats, AggPapers) {
	st := &Stats{Msgs: len(msgs)}
	uniqTitles := AggPapers{}
	for _, m := range msgs {
		papers, err := extractPapersFromMsg(m, authors)
		if err != nil {
			st.Errs++
			continue
		}
		st.Titles += len(papers)
		for _, paper := range papers {
			if !refs {
				paper.Refs = nil
			}
			if p, ok := uniqTitles[paper.Title]; ok {
				p.Freq += paper.Freq
				p.Refs = append(p.Refs, paper.Refs...)
			} else {
				uniqTitles[paper.Title] = paper
			}
		}
	}
	return st, uniqTitles
}
ExtractAndAggPapersFromMsgs parses mail messages and creates Papers, aggregated by title.
package papers
import (
	"bytes"
	"fmt"
	"log"
	"net/url"
	"regexp"
	"sort"
	"strings"
	"unicode"
	"unicode/utf8"
	"github.com/antchfx/htmlquery"
	"google.golang.org/api/gmail/v1"
	"github.com/bzz/scholar-alert-digest/gmailutils"
)
var scholarURLPrefix = regexp.MustCompile(`http(s)?: 
type Paper struct {
	Title    string
	URL      string
	Author   string `json:",omitempty"`
	Abstract Abstract
	Refs     []Ref `json:",omitempty"`
	Freq     int
}
type Ref struct {
	ID, Title string
}
type Abstract struct {
	FirstLine, Rest string
}
type AggPapers map[string]*Paper
type Stats struct {
	Msgs, Titles, Errs int
}
type sortedMap struct {
	m AggPapers
	s []string
}
func (sm *sortedMap) Len() int           { return len(sm.m) }
func (sm *sortedMap) Less(i, j int) bool { return sm.m[sm.s[i]].Freq > sm.m[sm.s[j]].Freq }
func (sm *sortedMap) Swap(i, j int)      { sm.s[i], sm.s[j] = sm.s[j], sm.s[i] }
func SortedKeys(m AggPapers) []string {
	sm := new(sortedMap)
	sm.m = m
	sm.s = make([]string, len(m))
	i := 0
	for key := range m {
		sm.s[i] = key
		i++
	}
	sort.Sort(sm)
	return sm.s
}