feat: implements today command

This commit is contained in:
Dennis Schoepf 2025-08-28 17:59:31 +02:00
parent 09f4b2a838
commit a77d4f9604
5 changed files with 101 additions and 14 deletions

View file

@ -25,7 +25,7 @@ freed add "https://feed.url"
Lists all currently stored feed with their metadata.
```sh
freed ls
freed list # or `freed ls`
```
### Show a feed entry for the day

View file

@ -46,11 +46,11 @@ var (
Short: "A f[r]eed aggregator with an intentionally reduced discovery mechanism.",
Long: `A f[r]eed aggregator with an intentionally reduced discovery mechanism.
f[r]eed aims to handle all sort of different inputs that emit recurring content. This includes: RSS feeds, youtube channels, and more. If there is a type of input that you wish to be handled (or you find a bug), open an issue at https://github.com/dennisschoepf/freed/issues.
f[r]eed aims to handle all sort of different inputs that emit recurring content. This includes: RSS feeds, youtube channels, and more. If there is a type of input that you wish to be handled (or you find a bug), file a issue at https://github.com/dennisschoepf/freed/issues.
Where f[r]eed is different to other feed aggregators or bookmarking services is in its discovery mechanism. Due to the overwhelming amount of content created every day, the constant stream of input can be overwhelming. That is why only a set amount of new content from your input feeds is shown to you when discovering new items. The amount is going to be configurable in the future.
The application also includes a set of recommended "Small Web" and topic-specific curated feeds. These are also available through the configuration.
The application will also include a set of recommended "Small Web" and topic-specific curated feeds.
`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
initDB()

View file

@ -1,5 +1,5 @@
/*
Copyright © 2024 Dennis Schoepf <dev@dnsc.io>
Copyright © 2025 Dennis Schoepf <dev@dnsc.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -17,26 +17,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package cmd
import (
"fmt"
"freed/internal"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)
// todayCmd represents the today command
var todayCmd = &cobra.Command{
Use: "today",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Aliases: []string{"day", "t"},
Short: "Shows an item for the day",
Long: `Shows an item from your stored feeds. Only allows for a set amount of items to be read in a day to discourage doom-scrolling.`,
Example: `freed today`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("today called")
url, err := internal.GetArticleUrlForToday()
if err != nil {
pterm.Error.Printf("Could not get article for today: %v\n", err)
return
}
title := pterm.LightCyan("Today's article")
pterm.DefaultBox.WithPadding(2).WithTitle(title).Println(url)
},
}
func init() {
rootCmd.AddCommand(todayCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// todayCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// todayCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

30
internal/article.go Normal file
View file

@ -0,0 +1,30 @@
package internal
import (
"fmt"
"freed/internal/database"
)
func GetArticleUrlForToday() (string, error) {
count, err := database.CountArticlesReadToday()
if err != nil {
return "", err
}
if count > 0 {
return "", fmt.Errorf("Already reached maximum number of articles for the day. Come back tomorrow!")
}
article, err := database.FindOneUnreadArticle()
if err != nil {
return "", err
}
if err := article.MarkAsRead(); err != nil {
return "", err
}
return article.Url, nil
}

View file

@ -5,6 +5,7 @@ import (
)
type Article struct {
ID string
Name string
Url string
ReadAt *time.Time
@ -27,6 +28,20 @@ func (a Article) Insert() (int64, error) {
return id, nil
}
func (a Article) MarkAsRead() error {
result, err := db.Exec("UPDATE article SET readAt = datetime() WHERE id = ?;", a.ID)
if err != nil {
return err
}
if _, err := result.RowsAffected(); err != nil {
return err
}
return nil
}
func InsertMultipleArticles(articles []Article) error {
if len(articles) == 0 {
return nil
@ -57,3 +72,29 @@ func InsertMultipleArticles(articles []Article) error {
return tx.Commit()
}
func FindOneUnreadArticle() (*Article, error) {
var article Article
row := db.QueryRow("SELECT * FROM article WHERE readAt IS NULL ORDER BY RANDOM() LIMIT 1")
err := row.Scan(&article.ID, &article.Name, &article.Url, &article.ReadAt, &article.FeedId)
if err != nil {
return nil, err
}
return &article, nil
}
func CountArticlesReadToday() (int, error) {
var count int
result := db.QueryRow("SELECT COUNT(*) FROM article WHERE date(readAt) = date()")
err := result.Scan(&count)
if err != nil {
return -1, err
}
return count, nil
}