This repository has been archived by the owner on Apr 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
cleaner.go
181 lines (148 loc) · 3.66 KB
/
cleaner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package main
import (
"fmt"
"log"
"strings"
"sync"
"time"
"github.com/ChimeraCoder/anaconda"
)
type Cleaner struct {
twitter *Twitter
tweetAge time.Duration
checkInterval time.Duration
dryRun bool
userID int64
ticker *time.Ticker
shutdown chan bool
}
func NewCleaner(twitter *Twitter, tweetAge, checkInterval time.Duration, dryRun bool) *Cleaner {
return &Cleaner{
twitter: twitter,
tweetAge: tweetAge,
checkInterval: checkInterval,
dryRun: dryRun,
ticker: time.NewTicker(checkInterval),
shutdown: make(chan bool),
}
}
func (c *Cleaner) Init() error {
userID, err := c.twitter.Self()
if err != nil {
return fmt.Errorf("failed to get user ID: %w", err)
}
c.userID = userID
return nil
}
func (c *Cleaner) Start() error {
var wg sync.WaitGroup
for {
select {
case <-c.ticker.C:
wg.Add(2)
if err := c.cleanTimeline(); err != nil {
return fmt.Errorf("failed to clean timeline: %w", err)
}
wg.Done()
if err := c.cleanFavorites(); err != nil {
return fmt.Errorf("failed to clean favorites: %w", err)
}
wg.Done()
case <-c.shutdown:
wg.Wait()
return nil
}
}
}
func (c *Cleaner) Stop() {
c.ticker.Stop()
c.shutdown <- true
}
func (c *Cleaner) cleanTimeline() error {
var oldestTweetID int64
for {
tweets, err := c.twitter.GetUserTimeline(oldestTweetID)
if err != nil {
return fmt.Errorf("failed to get tweets from user timeline: %w", err)
}
log.Printf("scanned through %d timeline tweets", len(tweets))
if len(tweets) <= 1 {
break
}
// get oldest tweet ID from the response
// and set it as a limit for the next call
oldestTweetID = tweets[len(tweets)-1].Id
for _, tweet := range tweets {
if err := c.remove(tweet); err != nil {
return fmt.Errorf("failed to remove tweet: %w", err)
}
}
}
return nil
}
func (c *Cleaner) cleanFavorites() error {
var oldestTweetID int64
for {
tweets, err := c.twitter.GetUserFavorites(oldestTweetID)
if err != nil {
return fmt.Errorf("failed to get tweets from favorites: %w", err)
}
log.Printf("scanned through %d favorite tweets", len(tweets))
if len(tweets) <= 1 {
break
}
// get oldest tweet ID from the response
// and set it as a limit for the next call
oldestTweetID = tweets[len(tweets)-1].Id
for _, tweet := range tweets {
if err := c.remove(tweet); err != nil {
return fmt.Errorf("failed to remove tweet: %w", err)
}
}
}
return nil
}
func (c *Cleaner) remove(tweet anaconda.Tweet) error {
createdAt, err := tweet.CreatedAtTime()
if err != nil {
return fmt.Errorf("failed to get createdAt time of a tweet: %w", err)
}
// if duration is less than retention period specified,
// skip it
if time.Since(createdAt) < c.tweetAge {
return nil
}
if tweet.Favorited {
log.Printf("UNFAVORITING\t%d", tweet.Id)
if !c.dryRun {
if err := c.twitter.UnFavorite(tweet.Id); err != nil {
// 144: No status found with that ID
if !strings.Contains(err.Error(), "144") {
return fmt.Errorf("failed to unfavorite the tweet %d: %w", tweet.Id, err)
}
}
}
}
if tweet.Retweeted {
log.Printf("UNRETWEETING\t%d", tweet.Id)
if !c.dryRun {
if err := c.twitter.UnRetweet(tweet.Id); err != nil {
return fmt.Errorf("failed to unretweet the tweet %d: %w", tweet.Id, err)
}
}
// retweeted tweet can't be deleted
return nil
}
// if tweet is not a users tweet,
// return earlier
if tweet.User.Id != c.userID {
return nil
}
log.Printf("DELETING\t%d", tweet.Id)
if !c.dryRun {
if err := c.twitter.Delete(tweet.Id); err != nil {
return fmt.Errorf("failed to delete the tweet %d: %w", tweet.Id, err)
}
}
return nil
}