Skip to content

Commit 8ef35ed

Browse files
committed
Added unsee subcommand.
Since moving to a new host it is harder to test when removing the state-file isn't a `rm` away. Allow expiring items from the history via the `rss2email unsee https:///` link. This triggers an email next time round, which confirms all is OK.
1 parent 4b70504 commit 8ef35ed

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

main.go

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func main() {
4343
subcommands.Register(&listCmd{})
4444
subcommands.Register(&listDefaultTemplateCmd{})
4545
subcommands.Register(&seenCmd{})
46+
subcommands.Register(&unseeCmd{})
4647
subcommands.Register(&versionCmd{})
4748

4849
//

unsee_cmd.go

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//
2+
// "Unsee" a feed item
3+
//
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"os"
10+
"path/filepath"
11+
12+
"github.com/skx/rss2email/state"
13+
"github.com/skx/subcommands"
14+
"go.etcd.io/bbolt"
15+
)
16+
17+
// Structure for our options and state.
18+
type unseeCmd struct {
19+
20+
// We embed the NoFlags option, because we accept no command-line flags.
21+
subcommands.NoFlags
22+
}
23+
24+
// Info is part of the subcommand-API.
25+
func (s *unseeCmd) Info() (string, string) {
26+
return "unsee", `Regard a feed item as new, and unseen.
27+
28+
This sub-command will allow you to mark an item as
29+
unseen, or new, meaning the next time the cron or daemon
30+
commands run they'll trigger an email notification.
31+
32+
This is useful in the case of testing.
33+
`
34+
}
35+
36+
//
37+
// Entry-point.
38+
//
39+
func (s *unseeCmd) Execute(args []string) int {
40+
41+
if len(args) < 1 {
42+
fmt.Printf("Please specify the URLs to unsee\n")
43+
return 1
44+
}
45+
46+
// Ensure we have a state-directory.
47+
dir := state.Directory()
48+
os.MkdirAll(dir, 0666)
49+
50+
// Now create the database, if missing, or open it if it exists.
51+
db, err := bbolt.Open(filepath.Join(dir, "state.db"), 0666, nil)
52+
if err != nil {
53+
fmt.Printf("Error opening database: %s\n", err.Error())
54+
return 1
55+
}
56+
57+
// Ensure we close when we're done
58+
defer db.Close()
59+
60+
// Keep track of buckets here
61+
var bucketNames [][]byte
62+
63+
// Record each bucket
64+
db.View(func(tx *bbolt.Tx) error {
65+
tx.ForEach(func(bucketName []byte, _ *bbolt.Bucket) error {
66+
bucketNames = append(bucketNames, bucketName)
67+
return nil
68+
})
69+
return nil
70+
})
71+
72+
// Process each bucket to find the item to remove.
73+
for _, buck := range bucketNames {
74+
75+
err = db.Update(func(tx *bbolt.Tx) error {
76+
77+
// Items to remove
78+
remove := []string{}
79+
80+
// Select the bucket, which we know must exist
81+
b := tx.Bucket([]byte(buck))
82+
83+
// Get a cursor to the key=value entries in the bucket
84+
c := b.Cursor()
85+
86+
// Iterate over the key/value pairs.
87+
for k, _ := c.First(); k != nil; k, _ = c.Next() {
88+
89+
// Convert the key to a string
90+
key := string(k)
91+
92+
// Is this something to remove?
93+
for _, arg := range args {
94+
95+
// If so append it.
96+
//
97+
// TODO: regexp?
98+
if arg == key {
99+
remove = append(remove, key)
100+
}
101+
}
102+
103+
}
104+
105+
// Now remove
106+
for _, key := range remove {
107+
b.Delete([]byte(key))
108+
}
109+
return nil
110+
})
111+
112+
if err != nil {
113+
fmt.Printf("error iterating over bucket %s: %s\n", buck, err.Error())
114+
return 1
115+
}
116+
}
117+
118+
return 0
119+
}

0 commit comments

Comments
 (0)