This repository has been archived by the owner on Jan 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmanager.go
270 lines (225 loc) · 8.21 KB
/
manager.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
package main
import (
"fmt"
"log"
"regexp"
"net/url"
"./smtp-with-self-signed-cert" // Standard lib can't support this *eye-roll*
"gopkg.in/gcfg.v1" // Config file parser
"github.com/ChimeraCoder/anaconda" // Twitter API
)
// Config
type Config struct {
Auth struct {
ConsumerKey string
ConsumerSecret string
AccessToken string
AccessTokenSecret string
}
Auth2 struct {
ConsumerKey string
ConsumerSecret string
AccessToken string
AccessTokenSecret string
}
Settings struct {
FriendsCount string
FollowerCount string
MentionsCount string
MyScreenName string
ReasonsURL string
}
Email struct {
SendEmails bool
Server string
ServerPort string
AuthUsername string
AuthPassword string
FromAddress string
AdminAddress string
}
}
// Load config, connect to Twitter API, fetch and check mentions,
// block and notify users where necessary
func main() {
// Get config from file
cfg := getConfig( "config.gcfg" )
// Initialise and authenticate API objects
anaconda.SetConsumerKey( cfg.Auth.ConsumerKey )
anaconda.SetConsumerSecret( cfg.Auth.ConsumerSecret )
api := anaconda.NewTwitterApi( cfg.Auth.AccessToken, cfg.Auth.AccessTokenSecret )
// Get friends' and followers' IDs
friendsIds := getFriendIDs( cfg.Settings.FriendsCount, api )
followersIds := getFollowersIDs( cfg.Settings.FollowerCount, api )
// Get mentions
mentions := getMentions( cfg.Settings.MentionsCount, api )
// Run through mentions checking against friend/follower lists and block criteria
for _ , tweet := range mentions {
fmt.Println( "<" + tweet.User.ScreenName + ">", tweet.Text )
// Check to see if tweet was posted by a friend or follower
if ! friendsIds[ tweet.User.Id ] && ! followersIds[ tweet.User.Id ] {
// Check they don't have a username starting with your username
match, err := regexp.MatchString( "(?i)^" + cfg.Settings.MyScreenName, tweet.User.ScreenName )
if err != nil { log.Fatalf( "Regexp failed: %s", err ) }
if match {
blockUser( tweet, "truncatedusername", cfg, api )
if cfg.Email.SendEmails {
emailNotification( tweet, "truncatedusername", cfg )
}
break
}
// Check the tweet content against block rules
checkTweet( tweet, cfg, api )
}
}
}
// Read config file
func getConfig( filename string ) ( *Config ) {
cfg := new( Config )
cfgerr := gcfg.ReadFileInto( cfg, filename )
if cfgerr != nil { log.Fatalf( "Failed to parse gcfg data: %s", cfgerr ) }
return( cfg )
}
// Get friends' user IDs
func getFriendIDs( friendsCount string, api *anaconda.TwitterApi ) ( map[int64]bool ) {
params := url.Values{}
params.Set( "count", friendsCount )
// Get the list of IDs from the API
friendsIdsAll, err := api.GetFriendsIds( params )
if err != nil { log.Fatalf( "Failed to get friends data: %s", err ) }
friendsIdsTemp := friendsIdsAll.Ids
// Create a map
friendsIds := make( map[int64]bool )
for _ , friendId := range friendsIdsTemp {
friendsIds[ friendId ] = true
}
return( friendsIds )
}
// Get followers' user IDs
func getFollowersIDs( followerCount string, api *anaconda.TwitterApi ) ( map[int64]bool ) {
params := url.Values{}
params.Set( "count", followerCount )
// Get the list of IDs from the API
followerIdsAll, err := api.GetFollowersIds( params )
if err != nil { log.Fatalf( "Failed to get followers data: %s", err ) }
followerIdsTemp := followerIdsAll.Ids
// Create a map
followerIds := make( map[int64]bool )
for _ , followerId := range followerIdsTemp {
followerIds[ followerId ] = true
}
return( followerIds )
}
// Get mentions
func getMentions( mentionsCount string, api *anaconda.TwitterApi ) ( []anaconda.Tweet ) {
params := url.Values{}
params.Set( "count", mentionsCount )
// TODO: Track tweet ID we checked up to last time, and check from there onwards
mentions, err := api.GetMentionsTimeline( params )
if err != nil { log.Fatalf( "Failed to get mentions: %s", err ) }
return( mentions )
}
// Check a tweet to see if it matches any of the block rules
// TODO: Pull the rules out into a config file (or database?)
func checkTweet( tweet anaconda.Tweet, cfg *Config, api *anaconda.TwitterApi ) {
// Body text
textRules := map[string]string {
// Celebrities
"(?i)Hamlin": "dennyhamlin", // Denny Hamlin - NASCAR driver
"(?i)NASCAR": "dennyhamlin", //
"(?i)Cagur": "dennycagur", // Denny Cagur - Indonesian actor
"(?i)Sumargo": "dennysumargo", // Denny Sumargo - Indonesian basketball player
"(?i)Gitong": "dennygitong", // Denny Gitong - Indonesian comedian
"(?i)Santoso": "dennysantoso", // Denny Santoso - Indonesian entrepreneur
// Denny's Diner
"(?i)^@Denny's$": "atdennys",
"(?i)@Denny's.+(breakfast|lunch|dinner|food|coffee|milkshake|Grand Slam|diner|waitress|service|smash|hungry|starving|IHOP)": "dennysdiner",
"(?i)(breakfast|lunch|dinner|food|coffee|milkshake|Grand Slam|diner|waitress|service|fam |smash|hungry|starving|IHOP|LIVE on #Periscope).+@Denny's": "dennysdiner" }
for rule, ruleName := range textRules {
match, err := regexp.MatchString( rule, tweet.Text )
if err != nil { log.Fatalf( "Regexp failed: %s", err ) }
if match {
blockUser( tweet, ruleName, cfg, api )
if cfg.Email.SendEmails {
emailNotification( tweet, ruleName, cfg )
}
return
}
}
// Location
locationRules := map[string]string {
// Indonesia in general is a problem for me on Twitter, unfortunately!
"(?i)Indonesia": "indonesia",
"(?i)Jakarta": "indonesia",
"(?i)Bandung": "indonesia",
"(?i)Padang": "indonesia",
}
for rule, ruleName := range locationRules {
location := tweet.Place.Country + " " + tweet.Place.Name + " " + tweet.Place.FullName
match, err := regexp.MatchString( rule, location )
if err != nil { log.Fatalf( "Regexp failed: %s", err ) }
if match {
blockUser( tweet, ruleName, cfg, api )
if cfg.Email.SendEmails {
emailNotification( tweet, ruleName, cfg )
}
return
}
}
}
// Block a user, and tweet a notification of why they were blocked
func blockUser( tweet anaconda.Tweet, ruleName string, cfg *Config, api *anaconda.TwitterApi ) {
// Block the user from the main account
user, err1 := api.BlockUserId( tweet.User.Id, nil )
if err1 != nil { log.Fatalf( "Failed to block user: %s", err1 ) }
// Let them know via the notification account
anaconda.SetConsumerKey( cfg.Auth2.ConsumerKey )
anaconda.SetConsumerSecret( cfg.Auth2.ConsumerSecret )
api2 := anaconda.NewTwitterApi( cfg.Auth2.AccessToken, cfg.Auth2.AccessTokenSecret )
// TODO: Make this work...
params := url.Values{}
params.Set( "InReplyToStatusID", tweet.IdStr )
params.Set( "InReplyToStatusIdStr", tweet.IdStr )
tweet2, err2 := api2.PostTweet( "@" + user.ScreenName +
": Hi! You've been blocked by @" + cfg.Settings.MyScreenName +
". Reason: " + cfg.Settings.ReasonsURL + "#" + ruleName, params )
if err2 != nil { log.Fatalf( "Failed to notify blocked user: %s", err2 ) }
// Display tweet in terminal
fmt.Println( ">> " + tweet2.Text )
// Restore API to main account auth settings
anaconda.SetConsumerKey( cfg.Auth.ConsumerKey )
anaconda.SetConsumerSecret( cfg.Auth.ConsumerSecret )
}
// Send an email letting admin know that bot did something
func emailNotification( tweet anaconda.Tweet, ruleName string, cfg *Config ) {
// Create the email body text
body := "From: " + cfg.Email.FromAddress + "\n" +
"To: " + cfg.Email.AdminAddress + "\n" +
"Subject: " + "[BlockBot] Blocked @" + tweet.User.ScreenName +
"\n" +
"Your block bot just blocked @" + tweet.User.ScreenName +
" for the following tweet: \n" +
tweet.Text + "\n" +
"\n" +
"User: https://twitter.com/" + tweet.User.ScreenName + "\n" +
"Tweet: https://twitter.com/" + tweet.User.ScreenName +
"/status/" + tweet.IdStr + "\n"
// Display email in terminal too
fmt.Println( body )
// Set up authentication details
auth := smtp.PlainAuth(
"",
cfg.Email.AuthUsername,
cfg.Email.AuthPassword,
cfg.Email.Server,
)
// Connect to SMTP server and send the email
err := smtp.SendMail(
cfg.Email.Server + ":" + cfg.Email.ServerPort,
auth,
cfg.Email.FromAddress,
[]string{ cfg.Email.AdminAddress },
[]byte( body ),
)
if err != nil { log.Fatal( err ) }
}