func articleReadState(user content.User, id data.ArticleId, read bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id, data.ArticleQueryOptions{SkipProcessors: true}) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyRead := in.Read if previouslyRead != read { article.Read(read) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyRead != read resp.val["Read"] = read resp.val["Id"] = in.Id return }
func articleFavoriteState(user content.User, id data.ArticleId, favorite bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id, data.ArticleQueryOptions{SkipProcessors: true}) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyFavorite := in.Favorite if previouslyFavorite != favorite { article.Favorite(favorite) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyFavorite != favorite resp.val["Favorite"] = favorite resp.val["Id"] = in.Id return }
func formatArticle(user content.User, id data.ArticleId, webfwConfig webfw.Config, readeefConfig readeef.Config) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } formatting := article.Format(webfwConfig.Renderer.Dir, readeefConfig.ArticleFormatter.ReadabilityKey) if article.HasErr() { resp.err = user.Err() return } s := summarize.NewFromString(formatting.Title, readeef.StripTags(formatting.Content)) s.Language = formatting.Language keyPoints := s.KeyPoints() for i := range keyPoints { keyPoints[i] = html.UnescapeString(keyPoints[i]) } resp.val["KeyPoints"] = keyPoints resp.val["Content"] = formatting.Content resp.val["TopImage"] = formatting.TopImage resp.val["Id"] = id return }
func markArticleAsFavorite(user content.User, id data.ArticleId, favorite bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyFavorite := in.Favorite if previouslyFavorite != favorite { article.Favorite(favorite) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyFavorite != favorite resp.val["Favorite"] = favorite resp.val["Id"] = in.Id return }
func markArticleAsRead(user content.User, id data.ArticleId, read bool) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } in := article.Data() previouslyRead := in.Read if previouslyRead != read { article.Read(read) if article.HasErr() { resp.err = article.Err() return } } resp.val["Success"] = previouslyRead != read resp.val["Read"] = read resp.val["Id"] = in.Id return }
func fetchArticle(user content.User, id data.ArticleId) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } resp.val["Article"] = article return }
func formatArticle(user content.User, id data.ArticleId, extractor content.Extractor, webfwConfig webfw.Config, readeefConfig readeef.Config) (resp responseError) { resp = newResponse() article := user.ArticleById(id) if user.HasErr() { resp.err = user.Err() return } extract := article.Extract() if article.HasErr() { resp.err = article.Err() return } extractData := extract.Data() if extract.HasErr() { switch err := extract.Err(); err { case content.ErrNoContent: if extractor == nil { resp.err = fmt.Errorf("Error formatting article: A valid extractor is reequired") return } extractData, resp.err = extractor.Extract(article.Data().Link) if resp.err != nil { return } extractData.ArticleId = article.Data().Id extract.Data(extractData) extract.Update() if extract.HasErr() { resp.err = extract.Err() return } default: resp.err = err return } } processors := user.Repo().ArticleProcessors() if len(processors) > 0 { a := user.Repo().UserArticle(user) a.Data(data.Article{Description: extractData.Content}) ua := []content.UserArticle{a} if extractData.TopImage != "" { a = user.Repo().UserArticle(user) a.Data(data.Article{ Description: fmt.Sprintf(`<img src="%s">`, extractData.TopImage), }) ua = append(ua, a) } for _, p := range processors { ua = p.ProcessArticles(ua) } extractData.Content = ua[0].Data().Description if extractData.TopImage != "" { content := ua[1].Data().Description content = strings.Replace(content, `<img src="`, "", -1) i := strings.Index(content, `"`) content = content[:i] extractData.TopImage = content } } s := summarize.NewFromString(extractData.Title, search.StripTags(extractData.Content)) s.Language = extractData.Language keyPoints := s.KeyPoints() for i := range keyPoints { keyPoints[i] = html.UnescapeString(keyPoints[i]) } resp.val["KeyPoints"] = keyPoints resp.val["Content"] = extractData.Content resp.val["TopImage"] = extractData.TopImage resp.val["Id"] = id return }
func (con Fever) Handler(c context.Context) http.Handler { repo := readeef.GetRepo(c) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logger := webfw.GetLogger(c) var err error var user content.User err = r.ParseForm() if err == nil { user = getReadeefUser(repo, r.FormValue("api_key"), webfw.GetLogger(c)) } resp := map[string]interface{}{"api_version": FEVER_API_VERSION} var reqType string switch { default: if user == nil { resp["auth"] = 0 break } now := time.Now().Unix() resp["auth"] = 1 resp["last_refreshed_on_time"] = now if _, ok := r.Form["groups"]; ok { reqType = "groups" logger.Infoln("Fetching fever groups") resp["groups"], resp["feeds_groups"], err = getGroups(user) } if _, ok := r.Form["feeds"]; ok { reqType = "feeds" logger.Infoln("Fetching fever feeds") var feverFeeds []feverFeed feeds := user.AllFeeds() err = user.Err() if err != nil { break } for i := range feeds { in := feeds[i].Data() feed := feverFeed{ Id: in.Id, Title: in.Title, Url: in.Link, SiteUrl: in.SiteLink, UpdateTime: now, } feverFeeds = append(feverFeeds, feed) } resp["feeds"] = feverFeeds _, resp["feeds_groups"], err = getGroups(user) } if _, ok := r.Form["unread_item_ids"]; ok { reqType = "unread item ids" logger.Infoln("Fetching unread fever item ids") ids := user.Ids(data.ArticleIdQueryOptions{UnreadOnly: true}) err = user.Err() if err != nil { break } buf := util.BufferPool.GetBuffer() defer util.BufferPool.Put(buf) for i := range ids { if i != 0 { buf.WriteString(",") } buf.WriteString(strconv.FormatInt(int64(ids[i]), 10)) } resp["unread_item_ids"] = buf.String() } if _, ok := r.Form["saved_item_ids"]; ok { reqType = "saved item ids" logger.Infoln("Fetching saved fever item ids") ids := user.Ids(data.ArticleIdQueryOptions{FavoriteOnly: true}) err = user.Err() if err != nil { break } buf := util.BufferPool.GetBuffer() defer util.BufferPool.Put(buf) for i := range ids { if i != 0 { buf.WriteString(",") } buf.WriteString(strconv.FormatInt(int64(ids[i]), 10)) } resp["saved_item_ids"] = buf.String() } if _, ok := r.Form["items"]; ok { reqType = "items" logger.Infoln("Fetching fever items") var count, since, max int64 count, err = user.Count(), user.Err() if err != nil { err = fmt.Errorf("Error getting user article count: %v", err) break } items := []feverItem{} if count > 0 { if val, ok := r.Form["since_id"]; ok { since, err = strconv.ParseInt(val[0], 10, 64) if err != nil { err = nil since = 0 } } if val, ok := r.Form["max_id"]; ok { max, err = strconv.ParseInt(val[0], 10, 64) if err != nil { err = nil since = 0 } } var articles []content.UserArticle // Fever clients do their own paging o := data.ArticleQueryOptions{Limit: 50, Offset: 0, SkipSessionProcessors: true} if withIds, ok := r.Form["with_ids"]; ok { stringIds := strings.Split(withIds[0], ",") ids := make([]data.ArticleId, 0, len(stringIds)) for _, stringId := range stringIds { stringId = strings.TrimSpace(stringId) if id, err := strconv.ParseInt(stringId, 10, 64); err == nil { ids = append(ids, data.ArticleId(id)) } } articles, err = user.ArticlesById(ids, data.ArticleQueryOptions{SkipSessionProcessors: true}), user.Err() } else if max > 0 { user.Order(data.DescendingOrder) o.BeforeId = data.ArticleId(max) articles, err = user.Articles(o), user.Err() } else { user.Order(data.AscendingOrder) o.AfterId = data.ArticleId(since) articles, err = user.Articles(o), user.Err() } if err != nil { break } for i := range articles { in := articles[i].Data() item := feverItem{ Id: in.Id, FeedId: in.FeedId, Title: in.Title, Html: in.Description, Url: in.Link, CreatedOnTime: in.Date.Unix(), } if in.Read { item.IsRead = 1 } if in.Favorite { item.IsSaved = 1 } items = append(items, item) } } resp["total_items"] = count resp["items"] = items } if _, ok := r.Form["links"]; ok { reqType = "links" logger.Infoln("Fetching fever links") offset, _ := strconv.ParseInt(r.FormValue("offset"), 10, 64) rng, e := strconv.ParseInt(r.FormValue("range"), 10, 64) if e != nil { rng = 7 } page := int64(1) page, err = strconv.ParseInt(r.FormValue("page"), 10, 64) if e != nil { break } if page > 3 { resp["links"] = []feverLink{} break } var articles []content.UserArticle var from, to time.Time if offset == 0 { from = time.Now().AddDate(0, 0, int(-1*rng)) to = time.Now() } else { from = time.Now().AddDate(0, 0, int(-1*rng-offset)) to = time.Now().AddDate(0, 0, int(-1*offset)) } user.SortingByDate() user.Order(data.DescendingOrder) articles, err = user.Articles(data.ArticleQueryOptions{ BeforeDate: to, AfterDate: from, Limit: 50, Offset: 50 * int(page-1), IncludeScores: true, }), user.Err() if err != nil { break } links := make([]feverLink, len(articles)) for i := range articles { in := articles[i].Data() link := feverLink{ Id: in.Id, FeedId: in.FeedId, ItemId: in.Id, IsItem: 1, IsLocal: 1, Title: in.Title, Url: in.Link, ItemIds: fmt.Sprintf("%d", in.Id), } if in.Score == 0 { link.Temperature = 0 } else { link.Temperature = math.Log10(float64(in.Score)) / math.Log10(1.1) } if in.Favorite { link.IsSaved = 1 } links[i] = link } resp["links"] = links } if val := r.PostFormValue("unread_recently_read"); val == "1" { reqType = "unread and recently read" logger.Infoln("Marking recently read fever items as unread") t := time.Now().Add(-24 * time.Hour) user.ReadState(false, data.ArticleUpdateStateOptions{ BeforeDate: time.Now(), AfterDate: t, }) err = user.Err() if err != nil { break } } if val := r.PostFormValue("mark"); val != "" { if val == "item" { logger.Infof("Marking fever item '%s' as '%s'\n", r.PostFormValue("id"), r.PostFormValue("as")) var id int64 var article content.UserArticle id, err = strconv.ParseInt(r.PostFormValue("id"), 10, 64) if err != nil { break } article, err = user.ArticleById(data.ArticleId(id), data.ArticleQueryOptions{SkipSessionProcessors: true}), user.Err() if err != nil { break } switch r.PostFormValue("as") { case "read": article.Read(true) case "saved": article.Favorite(true) case "unsaved": article.Favorite(false) default: err = errors.New("Unknown 'as' action") } if err == nil { err = article.Err() } } else if val == "feed" || val == "group" { logger.Infof("Marking fever %s '%s' as '%s'\n", val, r.PostFormValue("id"), r.PostFormValue("as")) if r.PostFormValue("as") != "read" { err = errors.New("Unknown 'as' action") break } var id, timestamp int64 id, err = strconv.ParseInt(r.PostFormValue("id"), 10, 64) if err != nil { break } timestamp, err = strconv.ParseInt(r.PostFormValue("before"), 10, 64) if err != nil { break } t := time.Unix(timestamp, 0) if val == "feed" { var feed content.UserFeed feed, err = user.FeedById(data.FeedId(id)), feed.Err() if err != nil { break } feed.ReadState(true, data.ArticleUpdateStateOptions{ BeforeDate: t, }) err = feed.Err() } else if val == "group" { if id == 1 || id == 0 { user.ReadState(true, data.ArticleUpdateStateOptions{ BeforeDate: t, }) err = user.Err() } else { err = errors.New(fmt.Sprintf("Unknown group %d\n", id)) } } } } } var b []byte if err == nil { b, err = json.Marshal(resp) } if err == nil { w.Write(b) } else { if reqType == "" { reqType = "modifying fever data" } else { reqType = "getting " + reqType + " for fever" } webfw.GetLogger(c).Print(fmt.Errorf("Error %s: %v", reqType, err)) w.WriteHeader(http.StatusInternalServerError) } }) }