diff --git a/src/content/readability/readability.go b/src/content/readability/readability.go index b7342bc..233810c 100644 --- a/src/content/readability/readability.go +++ b/src/content/readability/readability.go @@ -27,10 +27,16 @@ var ( blacklistCandidatesRegexp = regexp.MustCompile(`(?i)popupbody|-ad|g-plus`) okMaybeItsACandidateRegexp = regexp.MustCompile(`(?i)and|article|body|column|main|shadow`) - unlikelyCandidatesRegexp = regexp.MustCompile(`(?i)banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|modal|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote`) + unlikelyCandidatesRegexp = regexp.MustCompile( + `(?i)banner|breadcrumbs|combx|comment|community|cover-wrap|disqus|extra|foot|header|legends|menu|modal|related|remark|replies|rss|shoutbox|sidebar|skyscraper|social|sponsor|supplemental|ad-break|agegate|pagination|pager|popup|yom-remote`, + ) - negativeRegexp = regexp.MustCompile(`(?i)hidden|^hid$|hid$|hid|^hid |banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|modal|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget|byline|author|dateline|writtenby|p-author`) - positiveRegexp = regexp.MustCompile(`(?i)article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story`) + negativeRegexp = regexp.MustCompile( + `(?i)hidden|^hid$|hid$|hid|^hid |banner|combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|modal|outbrain|promo|related|scroll|share|shoutbox|sidebar|skyscraper|sponsor|shopping|tags|tool|widget|byline|author|dateline|writtenby|p-author`, + ) + positiveRegexp = regexp.MustCompile( + `(?i)article|body|content|entry|hentry|h-entry|main|page|pagination|post|text|blog|story`, + ) ) type nodeScores map[*html.Node]float32 diff --git a/src/content/sanitizer/sanitizer.go b/src/content/sanitizer/sanitizer.go index 673f7bc..1b1f6ab 100644 --- a/src/content/sanitizer/sanitizer.go +++ b/src/content/sanitizer/sanitizer.go @@ -146,7 +146,10 @@ func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([ } attrNames = append(attrNames, attribute.Key) - htmlAttrs = append(htmlAttrs, fmt.Sprintf(`%s="%s"`, attribute.Key, html.EscapeString(value))) + htmlAttrs = append( + htmlAttrs, + fmt.Sprintf(`%s="%s"`, attribute.Key, html.EscapeString(value)), + ) } extraAttrNames, extraHTMLAttributes := getExtraAttributes(tagName) @@ -161,11 +164,25 @@ func sanitizeAttributes(baseURL, tagName string, attributes []html.Attribute) ([ func getExtraAttributes(tagName string) ([]string, []string) { switch tagName { case "a": - return []string{"rel", "target", "referrerpolicy"}, []string{`rel="noopener noreferrer"`, `target="_blank"`, `referrerpolicy="no-referrer"`} + return []string{ + "rel", + "target", + "referrerpolicy", + }, []string{ + `rel="noopener noreferrer"`, + `target="_blank"`, + `referrerpolicy="no-referrer"`, + } case "video", "audio": return []string{"controls"}, []string{"controls"} case "iframe": - return []string{"sandbox", "loading"}, []string{`sandbox="allow-scripts allow-same-origin allow-popups"`, `loading="lazy"`} + return []string{ + "sandbox", + "loading", + }, []string{ + `sandbox="allow-scripts allow-same-origin allow-popups"`, + `loading="lazy"`, + } case "img": return []string{"loading"}, []string{`loading="lazy"`, `referrerpolicy="no-referrer"`} default: diff --git a/src/parser/atom.go b/src/parser/atom.go index 607aa04..4434eff 100644 --- a/src/parser/atom.go +++ b/src/parser/atom.go @@ -91,13 +91,22 @@ func ParseAtom(r io.Reader) (*Feed, error) { mediaLinks := srcitem.mediaLinks() - link := firstNonEmpty(srcitem.OrigLink, srcitem.Links.First("alternate"), srcitem.Links.First(""), linkFromID) + link := firstNonEmpty( + srcitem.OrigLink, + srcitem.Links.First("alternate"), + srcitem.Links.First(""), + linkFromID, + ) dstfeed.Items = append(dstfeed.Items, Item{ - GUID: firstNonEmpty(guidFromID, srcitem.ID, link), - Date: dateParse(firstNonEmpty(srcitem.Published, srcitem.Updated)), - URL: link, - Title: srcitem.Title.Text(), - Content: firstNonEmpty(srcitem.Content.String(), srcitem.Summary.String(), srcitem.firstMediaDescription()), + GUID: firstNonEmpty(guidFromID, srcitem.ID, link), + Date: dateParse(firstNonEmpty(srcitem.Published, srcitem.Updated)), + URL: link, + Title: srcitem.Title.Text(), + Content: firstNonEmpty( + srcitem.Content.String(), + srcitem.Summary.String(), + srcitem.firstMediaDescription(), + ), MediaLinks: mediaLinks, }) } diff --git a/src/parser/feed_test.go b/src/parser/feed_test.go index d24f3b5..25aa48c 100644 --- a/src/parser/feed_test.go +++ b/src/parser/feed_test.go @@ -40,7 +40,12 @@ func TestSniff(t *testing.T) { want := testcase.want have := sniff(testcase.input) if want.encoding != have.encoding || want.feedType != have.feedType { - t.Errorf("Invalid output\n---\n%s\n---\n\nwant=%#v\nhave=%#v", testcase.input, want, have) + t.Errorf( + "Invalid output\n---\n%s\n---\n\nwant=%#v\nhave=%#v", + testcase.input, + want, + have, + ) } } } diff --git a/src/parser/media.go b/src/parser/media.go index e82c2c6..6be4222 100644 --- a/src/parser/media.go +++ b/src/parser/media.go @@ -70,7 +70,10 @@ func (m *media) mediaLinks() []MediaLink { } else if strings.HasPrefix(content.MediaType, "video/") { links = append(links, MediaLink{URL: url, Type: "video", Description: description}) } else if content.MediaMedium == "image" || content.MediaMedium == "audio" || content.MediaMedium == "video" { - links = append(links, MediaLink{URL: url, Type: content.MediaMedium, Description: description}) + links = append( + links, + MediaLink{URL: url, Type: content.MediaMedium, Description: description}, + ) } else { if len(content.MediaThumbnails) > 0 { links = append(links, MediaLink{ diff --git a/src/parser/rdf_test.go b/src/parser/rdf_test.go index 0a99c25..14494a1 100644 --- a/src/parser/rdf_test.go +++ b/src/parser/rdf_test.go @@ -42,8 +42,16 @@ func TestRDFFeed(t *testing.T) { Title: "Mozilla Dot Org", SiteURL: "http://www.mozilla.org", Items: []Item{ - {GUID: "http://www.mozilla.org/status/", URL: "http://www.mozilla.org/status/", Title: "New Status Updates"}, - {GUID: "http://www.mozilla.org/bugs/", URL: "http://www.mozilla.org/bugs/", Title: "Bugzilla Reorganized"}, + { + GUID: "http://www.mozilla.org/status/", + URL: "http://www.mozilla.org/status/", + Title: "New Status Updates", + }, + { + GUID: "http://www.mozilla.org/bugs/", + URL: "http://www.mozilla.org/bugs/", + Title: "Bugzilla Reorganized", + }, }, } diff --git a/src/parser/rss.go b/src/parser/rss.go index f941cac..1470bfc 100644 --- a/src/parser/rss.go +++ b/src/parser/rss.go @@ -78,7 +78,8 @@ func ParseRSS(r io.Reader) (*Feed, error) { for _, e := range srcitem.Enclosures { if strings.HasPrefix(e.Type, "audio/") { podcastURL := e.URL - if srcitem.OrigEnclosureLink != "" && strings.Contains(podcastURL, path.Base(srcitem.OrigEnclosureLink)) { + if srcitem.OrigEnclosureLink != "" && + strings.Contains(podcastURL, path.Base(srcitem.OrigEnclosureLink)) { podcastURL = srcitem.OrigEnclosureLink } mediaLinks = append(mediaLinks, MediaLink{URL: podcastURL, Type: "audio"}) @@ -97,11 +98,15 @@ func ParseRSS(r io.Reader) (*Feed, error) { } dstfeed.Items = append(dstfeed.Items, Item{ - GUID: firstNonEmpty(srcitem.GUID.GUID, srcitem.Link), - Date: dateParse(firstNonEmpty(srcitem.DublinCoreDate, srcitem.PubDate)), - URL: firstNonEmpty(srcitem.OrigLink, srcitem.Link, permalink), - Title: srcitem.Title, - Content: firstNonEmpty(srcitem.ContentEncoded, srcitem.Description, srcitem.firstMediaDescription()), + GUID: firstNonEmpty(srcitem.GUID.GUID, srcitem.Link), + Date: dateParse(firstNonEmpty(srcitem.DublinCoreDate, srcitem.PubDate)), + URL: firstNonEmpty(srcitem.OrigLink, srcitem.Link, permalink), + Title: srcitem.Title, + Content: firstNonEmpty( + srcitem.ContentEncoded, + srcitem.Description, + srcitem.firstMediaDescription(), + ), MediaLinks: mediaLinks, }) } diff --git a/src/parser/rss_test.go b/src/parser/rss_test.go index 8ce413a..4105eb8 100644 --- a/src/parser/rss_test.go +++ b/src/parser/rss_test.go @@ -303,9 +303,21 @@ func TestRSSMultipleMedia(t *testing.T) { GUID: "http://example.com/posts/1", URL: "http://example.com/posts/1", MediaLinks: []MediaLink{ - {URL: "https://example.com/path/to/image1.png", Type: "image", Description: "description 1"}, - {URL: "https://example.com/path/to/image2.png", Type: "image", Description: "description 2"}, - {URL: "https://example.com/path/to/video1.mp4", Type: "video", Description: "video description"}, + { + URL: "https://example.com/path/to/image1.png", + Type: "image", + Description: "description 1", + }, + { + URL: "https://example.com/path/to/image2.png", + Type: "image", + Description: "description 2", + }, + { + URL: "https://example.com/path/to/video1.mp4", + Type: "video", + Description: "video description", + }, }, }, } diff --git a/src/server/opml/read_test.go b/src/server/opml/read_test.go index ae82e1c..a0041e0 100644 --- a/src/server/opml/read_test.go +++ b/src/server/opml/read_test.go @@ -78,7 +78,11 @@ func TestParseFallback(t *testing.T) { Folders: []Folder{{ Title: "foldertitle", Feeds: []Feed{ - {Title: "feedtext", FeedUrl: "https://example.com/feed.xml", SiteUrl: "https://example.com"}, + { + Title: "feedtext", + FeedUrl: "https://example.com/feed.xml", + SiteUrl: "https://example.com", + }, }, }}, } diff --git a/src/server/routes.go b/src/server/routes.go index 9f12a1c..37c1368 100644 --- a/src/server/routes.go +++ b/src/server/routes.go @@ -77,7 +77,8 @@ func (s *Server) handleStatic(c *router.Context) { c.Out.WriteHeader(http.StatusNotFound) return } - http.StripPrefix(s.BasePath+"/static/", http.FileServer(http.FS(assets.FS))).ServeHTTP(c.Out, c.Req) + http.StripPrefix(s.BasePath+"/static/", http.FileServer(http.FS(assets.FS))). + ServeHTTP(c.Out, c.Req) } func (s *Server) handleManifest(c *router.Context) { @@ -236,7 +237,10 @@ func (s *Server) handleFeedList(c *router.Context) { log.Printf("Faild to discover feed for %s: %s", form.Url, err) c.JSON(http.StatusOK, map[string]string{"status": "notfound"}) case len(result.Sources) > 0: - c.JSON(http.StatusOK, map[string]interface{}{"status": "multiple", "choice": result.Sources}) + c.JSON( + http.StatusOK, + map[string]interface{}{"status": "multiple", "choice": result.Sources}, + ) case result.Feed != nil: feed := s.db.CreateFeed( result.Feed.Title, diff --git a/src/server/util_test.go b/src/server/util_test.go index b863001..fd61015 100644 --- a/src/server/util_test.go +++ b/src/server/util_test.go @@ -28,4 +28,4 @@ func TestIsInternalFromURL(t *testing.T) { t.Errorf("isInternalFromURL(%q) = %v; want %v", test.url, result, test.expected) } } -} \ No newline at end of file +} diff --git a/src/storage/item.go b/src/storage/item.go index 960d873..474c8b9 100644 --- a/src/storage/item.go +++ b/src/storage/item.go @@ -191,7 +191,10 @@ func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []interfac terms[idx] = word + "*" } - cond = append(cond, "i.search_rowid in (select rowid from search where search match :search)") + cond = append( + cond, + "i.search_rowid in (select rowid from search where search match :search)", + ) args = append(args, sql.Named("search", strings.Join(terms, " "))) } if filter.After != nil { @@ -199,7 +202,13 @@ func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []interfac if newestFirst { compare = "<" } - cond = append(cond, fmt.Sprintf("(i.date, i.id) %s (select date, id from items where id = :after_id)", compare)) + cond = append( + cond, + fmt.Sprintf( + "(i.date, i.id) %s (select date, id from items where id = :after_id)", + compare, + ), + ) args = append(args, sql.Named("after_id", *filter.After)) } if filter.IDs != nil && len(*filter.IDs) > 0 { @@ -249,7 +258,12 @@ func (s *Storage) CountItems(filter ItemFilter) int { return count } -func (s *Storage) ListItems(filter ItemFilter, limit int, newestFirst bool, withContent bool) []Item { +func (s *Storage) ListItems( + filter ItemFilter, + limit int, + newestFirst bool, + withContent bool, +) []Item { predicate, args := listQueryPredicate(filter, newestFirst) result := make([]Item, 0) @@ -450,7 +464,8 @@ func (s *Storage) DeleteOldItems() { } for feedId, limit := range feedLimits { - result, err := s.db.Exec(` + result, err := s.db.Exec( + ` delete from items where id in ( select i.id @@ -463,7 +478,10 @@ func (s *Storage) DeleteOldItems() { sql.Named("feed_id", feedId), sql.Named("starred_status", STARRED), sql.Named("limit", limit), - sql.Named("date_limit", time.Now().UTC().Add(-time.Hour*time.Duration(24*itemsKeepDays))), + sql.Named( + "date_limit", + time.Now().UTC().Add(-time.Hour*time.Duration(24*itemsKeepDays)), + ), ) if err != nil { log.Print(err) diff --git a/src/storage/item_test.go b/src/storage/item_test.go index a11cf83..dd5b060 100644 --- a/src/storage/item_test.go +++ b/src/storage/item_test.go @@ -47,21 +47,62 @@ func testItemsSetup(db *Storage) testItemScope { db.CreateItems([]Item{ // feed11 {GUID: "item111", FeedId: feed11.Id, Title: "title111", Date: now.Add(time.Hour * 24 * 1)}, - {GUID: "item112", FeedId: feed11.Id, Title: "title112", Date: now.Add(time.Hour * 24 * 2)}, // read - {GUID: "item113", FeedId: feed11.Id, Title: "title113", Date: now.Add(time.Hour * 24 * 3)}, // starred + { + GUID: "item112", + FeedId: feed11.Id, + Title: "title112", + Date: now.Add(time.Hour * 24 * 2), + }, // read + { + GUID: "item113", + FeedId: feed11.Id, + Title: "title113", + Date: now.Add(time.Hour * 24 * 3), + }, // starred // feed12 {GUID: "item121", FeedId: feed12.Id, Title: "title121", Date: now.Add(time.Hour * 24 * 4)}, - {GUID: "item122", FeedId: feed12.Id, Title: "title122", Date: now.Add(time.Hour * 24 * 5)}, // read + { + GUID: "item122", + FeedId: feed12.Id, + Title: "title122", + Date: now.Add(time.Hour * 24 * 5), + }, // read // feed21 - {GUID: "item211", FeedId: feed21.Id, Title: "title211", Date: now.Add(time.Hour * 24 * 6)}, // read - {GUID: "item212", FeedId: feed21.Id, Title: "title212", Date: now.Add(time.Hour * 24 * 7)}, // starred + { + GUID: "item211", + FeedId: feed21.Id, + Title: "title211", + Date: now.Add(time.Hour * 24 * 6), + }, // read + { + GUID: "item212", + FeedId: feed21.Id, + Title: "title212", + Date: now.Add(time.Hour * 24 * 7), + }, // starred // feed01 {GUID: "item011", FeedId: feed01.Id, Title: "title011", Date: now.Add(time.Hour * 24 * 8)}, - {GUID: "item012", FeedId: feed01.Id, Title: "title012", Date: now.Add(time.Hour * 24 * 9)}, // read - {GUID: "item013", FeedId: feed01.Id, Title: "title013", Date: now.Add(time.Hour * 24 * 10)}, // starred + { + GUID: "item012", + FeedId: feed01.Id, + Title: "title012", + Date: now.Add(time.Hour * 24 * 9), + }, // read + { + GUID: "item013", + FeedId: feed01.Id, + Title: "title013", + Date: now.Add(time.Hour * 24 * 10), + }, // starred }) - db.db.Exec(`update items set status = :status where guid in ("item112", "item122", "item211", "item012")`, sql.Named("status", READ)) - db.db.Exec(`update items set status = :status where guid in ("item113", "item212", "item013")`, sql.Named("status", STARRED)) + db.db.Exec( + `update items set status = :status where guid in ("item112", "item122", "item211", "item012")`, + sql.Named("status", READ), + ) + db.db.Exec( + `update items set status = :status where guid in ("item113", "item212", "item013")`, + sql.Named("status", STARRED), + ) return testItemScope{ feed11: feed11, @@ -208,7 +249,9 @@ func TestListItemsPaginated(t *testing.T) { // unread, newest first unread := UNREAD - have = getItemGuids(db.ListItems(ItemFilter{After: &item012.Id, Status: &unread}, 3, true, false)) + have = getItemGuids( + db.ListItems(ItemFilter{After: &item012.Id, Status: &unread}, 3, true, false), + ) want = []string{"item011", "item121", "item111"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) @@ -218,7 +261,9 @@ func TestListItemsPaginated(t *testing.T) { // starred, oldest first starred := STARRED - have = getItemGuids(db.ListItems(ItemFilter{After: &item121.Id, Status: &starred}, 3, false, false)) + have = getItemGuids( + db.ListItems(ItemFilter{After: &item121.Id, Status: &starred}, 3, false, false), + ) want = []string{"item212", "item013"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) diff --git a/src/systray/systray_windows.go b/src/systray/systray_windows.go index ecd3b90..d4c2ee7 100644 --- a/src/systray/systray_windows.go +++ b/src/systray/systray_windows.go @@ -248,7 +248,11 @@ var wt winTray // WindowProc callback function that processes messages sent to a window. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms633573(v=vs.85).aspx -func (t *winTray) wndProc(hWnd windows.Handle, message uint32, wParam, lParam uintptr) (lResult uintptr) { +func (t *winTray) wndProc( + hWnd windows.Handle, + message uint32, + wParam, lParam uintptr, +) (lResult uintptr) { const ( WM_RBUTTONUP = 0x0205 WM_LBUTTONUP = 0x0202 @@ -494,7 +498,12 @@ func (t *winTray) convertToSubMenu(menuItemId uint32) (windows.Handle, error) { return menu, nil } -func (t *winTray) addOrUpdateMenuItem(menuItemId uint32, parentId uint32, title string, disabled, checked bool) error { +func (t *winTray) addOrUpdateMenuItem( + menuItemId uint32, + parentId uint32, + title string, + disabled, checked bool, +) error { // https://msdn.microsoft.com/en-us/library/windows/desktop/ms647578(v=vs.85).aspx const ( MIIM_FTYPE = 0x00000100 @@ -888,7 +897,13 @@ func (item *MenuItem) SetIcon(iconBytes []byte) { wt.menuItemIcons[uint32(item.id)] = h wt.muMenuItemIcons.Unlock() - err = wt.addOrUpdateMenuItem(uint32(item.id), item.parentId(), item.title, item.disabled, item.checked) + err = wt.addOrUpdateMenuItem( + uint32(item.id), + item.parentId(), + item.title, + item.disabled, + item.checked, + ) if err != nil { log.Printf("Unable to addOrUpdateMenuItem: %v", err) return @@ -905,7 +920,13 @@ func SetTooltip(tooltip string) { } func addOrUpdateMenuItem(item *MenuItem) { - err := wt.addOrUpdateMenuItem(uint32(item.id), item.parentId(), item.title, item.disabled, item.checked) + err := wt.addOrUpdateMenuItem( + uint32(item.id), + item.parentId(), + item.title, + item.disabled, + item.checked, + ) if err != nil { log.Printf("Unable to addOrUpdateMenuItem: %v", err) return