snoo 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/flairbot/README.md +23 -0
- data/examples/flairbot/config.yaml +27 -0
- data/examples/flairbot/flair.rb +108 -0
- data/lib/snoo.rb +1 -1
- data/lib/snoo/account.rb +16 -4
- data/lib/snoo/flair.rb +1 -1
- data/lib/snoo/utilities.rb +10 -9
- data/lib/snoo/version.rb +1 -1
- metadata +5 -2
@@ -0,0 +1,23 @@
|
|
1
|
+
This is a simple flairbot you can use to automatically add flair to a subreddit.
|
2
|
+
|
3
|
+
It is currently in use on [r/TheWalkingDead](http://www.reddit.com/r/thewalkingdead) and [r/BreakingBad](http://www.reddit.com/r/breakingbad). It provides automatic spoiler detection
|
4
|
+
|
5
|
+
# Configuration
|
6
|
+
|
7
|
+
## Account
|
8
|
+
Configuring the bot's account is very simple. Simply edit `config.yaml` and fill out the username and password fields.
|
9
|
+
|
10
|
+
## Subreddits
|
11
|
+
Each subreddit can have multiple regexes, and the bot can run on multiple subreddits. It needs to have moderator on all subreddits.
|
12
|
+
|
13
|
+
To add new subreddits, make a new first-level entry under `subreddits:`
|
14
|
+
|
15
|
+
To add new flair parsers, add a new entry, with a regular expression as the name. Ex `(show|comic) spoilers`. Beneath this, set if you want to ignore case (recommended) and the flair id of the linkflair to apply. This shows up if you inspect the HTML source of the reddit flair dialog on links.
|
16
|
+
|
17
|
+
# Caveats
|
18
|
+
|
19
|
+
This bot scrapes new, and as such, will ususally catch most things. That said, it is imperfect, and things may slip through.
|
20
|
+
|
21
|
+
To prevent re-duplicating flair calls, it hides a post on flair. This allows you to see which post a bot has flaired, simply by logging into its account.
|
22
|
+
|
23
|
+
Finally, regexes are applied in a top-down cascade. Regexes at the bottom will override ones at the top. A post can only have one linkflair, so go from general to specific in your regexes.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Batch size, cannot go above 100
|
2
|
+
limit: 100
|
3
|
+
# Login details
|
4
|
+
username: flair-o-tron
|
5
|
+
password:
|
6
|
+
# Logging
|
7
|
+
logging: false
|
8
|
+
# Uncomment the following line to enable logging to file
|
9
|
+
# logfile: /home/paradox/ruby/log/flair.log
|
10
|
+
# Log rotation
|
11
|
+
logrotate: weekly
|
12
|
+
# Your subreddits
|
13
|
+
# Just copy and paste what you see
|
14
|
+
subreddits:
|
15
|
+
thewalkingdead:
|
16
|
+
'comic.spoiler':
|
17
|
+
ignore_case: true
|
18
|
+
id: 'e9452e44-846f-11e1-994b-12313d2c1af1'
|
19
|
+
'[(show)(tv)].spoiler':
|
20
|
+
ignore_case: true
|
21
|
+
id: 'ed8c5612-846f-11e1-837a-12313b08a511'
|
22
|
+
breakingbad:
|
23
|
+
'spoiler':
|
24
|
+
ignore_case: true
|
25
|
+
id: '083ccbc2-ce0d-11e1-85b5-12313b0c247a'
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'yaml'
|
3
|
+
require 'snoo'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
class String
|
7
|
+
def colorize(color, options = {})
|
8
|
+
background = options[:background] || options[:bg] || false
|
9
|
+
style = options[:style]
|
10
|
+
offsets = ["gray","red", "green", "yellow", "blue", "magenta", "cyan","white"]
|
11
|
+
styles = ["normal","bold","dark","italic","underline","xx","xx","underline","xx","strikethrough"]
|
12
|
+
start = background ? 40 : 30
|
13
|
+
color_code = start + (offsets.index(color) || 8)
|
14
|
+
style_code = styles.index(style) || 0
|
15
|
+
"\e[#{style_code};#{color_code}m#{self}\e[0m"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
config = OpenStruct.new YAML.load(File.read("/home/paradox/ruby/config.yaml"))
|
20
|
+
|
21
|
+
log = config.logfile.nil? ? Logger.new(STDOUT) : Logger.new(config.logfile, config.logrotate)
|
22
|
+
|
23
|
+
log.level = config.logging ? Logger::DEBUG : Logger::FATAL
|
24
|
+
|
25
|
+
log.info("Starting log on #{Time.now}")
|
26
|
+
reddit = Snoo::Client.new
|
27
|
+
log.debug('Logging into reddit')
|
28
|
+
reddit.log_in config.username, config.password
|
29
|
+
log.debug('Success!')
|
30
|
+
|
31
|
+
queue = Queue.new
|
32
|
+
retried = nil
|
33
|
+
config.subreddits.each do |subreddit, flairs|
|
34
|
+
begin
|
35
|
+
log.debug("Getting #{config.limit} new links for #{subreddit}")
|
36
|
+
listing = reddit.get_listing(subreddit: subreddit, page: 'new', sort: 'new', limit: config.limit)['data']['children']
|
37
|
+
log.debug("Getting #{config.limit} hot links for #{subreddit}")
|
38
|
+
listing.concat reddit.get_listing(subreddit: subreddit, limit: config.limit)['data']['children']
|
39
|
+
listing.uniq!
|
40
|
+
rescue
|
41
|
+
log.error("Got error #{$!}".colorize "red")
|
42
|
+
if !retried.nil?
|
43
|
+
retried = true
|
44
|
+
log.debug('Trying again')
|
45
|
+
retry
|
46
|
+
else
|
47
|
+
next
|
48
|
+
retried = nil
|
49
|
+
end
|
50
|
+
ensure
|
51
|
+
retried = nil
|
52
|
+
end
|
53
|
+
flairs.each do |reg, options|
|
54
|
+
r = Regexp.new reg, options['ignore_case']
|
55
|
+
log.debug("Matching against #{r}")
|
56
|
+
listing.each do |thing|
|
57
|
+
# binding.pry
|
58
|
+
if thing['data']['title'] =~ r
|
59
|
+
q = {
|
60
|
+
subreddit: subreddit,
|
61
|
+
template: options['id'],
|
62
|
+
thing: 't3_' + thing['data']['id']
|
63
|
+
}
|
64
|
+
log.debug("Queueing #{q}")
|
65
|
+
queue << q
|
66
|
+
log.debug("Hiding (#{thing['data']['id']}) #{thing['data']['title']}")
|
67
|
+
begin
|
68
|
+
reddit.hide 't3_' + thing['data']['id']
|
69
|
+
sleep 2
|
70
|
+
rescue
|
71
|
+
log.error("Got error #{$!}".colorize "red")
|
72
|
+
if retried.nil?
|
73
|
+
retried = true
|
74
|
+
sleep 5
|
75
|
+
log.debug('Trying again')
|
76
|
+
retry
|
77
|
+
else
|
78
|
+
next
|
79
|
+
end
|
80
|
+
ensure
|
81
|
+
retried = nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
sleep 2
|
87
|
+
end
|
88
|
+
|
89
|
+
queue.length.times do
|
90
|
+
flair = queue.pop
|
91
|
+
begin
|
92
|
+
log.debug("Setting flair on #{flair}")
|
93
|
+
reddit.select_flair_template( flair[:template], flair[:subreddit], link: flair[:thing])
|
94
|
+
sleep 2 # For reddit api limits, we sleep 2
|
95
|
+
rescue
|
96
|
+
log.error("Got error #{$!}".colorize "red")
|
97
|
+
if retried.nil?
|
98
|
+
retried = true
|
99
|
+
sleep 5
|
100
|
+
log.debug('Trying again')
|
101
|
+
retry
|
102
|
+
else
|
103
|
+
next
|
104
|
+
end
|
105
|
+
ensure
|
106
|
+
retried = nil
|
107
|
+
end
|
108
|
+
end
|
data/lib/snoo.rb
CHANGED
data/lib/snoo/account.rb
CHANGED
@@ -13,17 +13,29 @@ module Snoo
|
|
13
13
|
login = post("/api/login", :body => {user: username, passwd: password, api_type: 'json'})
|
14
14
|
errors = login['json']['errors']
|
15
15
|
raise errors[0][1] unless errors.size == 0
|
16
|
-
|
16
|
+
set_cookies login.headers['set-cookie']
|
17
17
|
@modhash = login['json']['data']['modhash']
|
18
18
|
@username = username
|
19
19
|
@userid = 't2_' + get('/api/me.json')['data']['id']
|
20
20
|
return login
|
21
21
|
end
|
22
22
|
|
23
|
+
# Auth into reddit via modhash and cookie. This has the advantage of not throttling you if you call it a lot
|
24
|
+
#
|
25
|
+
# @param modhash [String] The modhash to use
|
26
|
+
# @param cookies [String] The cookies string to give to the header
|
27
|
+
def auth modhash, cookies
|
28
|
+
set_cookies cookies
|
29
|
+
@modhash = modhash
|
30
|
+
meinfo = get("/api/me.json")
|
31
|
+
@username = meinfo['data']['name']
|
32
|
+
@userid = 't2_' + meinfo['data']['id']
|
33
|
+
end
|
34
|
+
|
23
35
|
# Logs out of a reddit account. This is usually uneeded, you can just log_in as a new account to replace the current one.
|
24
36
|
# This just nils the cookies and modhash
|
25
37
|
def log_out
|
26
|
-
|
38
|
+
set_cookies nil
|
27
39
|
@modhash = nil
|
28
40
|
@userid = nil
|
29
41
|
@username = nil
|
@@ -38,7 +50,7 @@ module Snoo
|
|
38
50
|
def clear_sessions password
|
39
51
|
logged_in?
|
40
52
|
clear = post('/api/clear_sessions', body: { curpass: password, dest: @baseurl, uh: @modhash })
|
41
|
-
|
53
|
+
set_cookies clear.headers['set-cookie']
|
42
54
|
return clear
|
43
55
|
end
|
44
56
|
|
@@ -85,7 +97,7 @@ module Snoo
|
|
85
97
|
}
|
86
98
|
params[:email] = email if email
|
87
99
|
update = post('/api/update', body: params )
|
88
|
-
|
100
|
+
set_cookies update.headers['set-cookie']
|
89
101
|
return update
|
90
102
|
end
|
91
103
|
end
|
data/lib/snoo/flair.rb
CHANGED
@@ -42,7 +42,7 @@ module Snoo
|
|
42
42
|
# @param opts [Hash] An options hash.
|
43
43
|
# @option opts [String] :css_class The class(es) applied to the flair. Whitespace separated
|
44
44
|
# @option opts [String] :text The flair text
|
45
|
-
# @option opts [String] :name The user who we are flairing. This requires a username
|
45
|
+
# @option opts [String] :name The user who we are flairing. This requires a username
|
46
46
|
# @option opts [String] :link The thing id of the link (if a link). Begins with `t3_`
|
47
47
|
# @return (see #clear_sessions)
|
48
48
|
def flair subreddit, opts = {}
|
data/lib/snoo/utilities.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
module Snoo
|
2
2
|
# Utility functions.
|
3
|
-
# These are all private
|
4
3
|
#
|
5
4
|
# @author (see Snoo)
|
6
5
|
module Utilities
|
6
|
+
|
7
|
+
# Set the cookie header and instance variable
|
8
|
+
#
|
9
|
+
# @param cookie [String] The cookie text, as show in a 'set-cookie' header
|
10
|
+
def set_cookies cookie
|
11
|
+
@cookies = cookie
|
12
|
+
self.class.headers 'Cookie' => cookie
|
13
|
+
end
|
14
|
+
|
7
15
|
private
|
8
16
|
# HTTParty get wrapper. This serves to clean up code, as well as throw webserver errors wherever needed
|
9
17
|
#
|
@@ -20,18 +28,11 @@ module Snoo
|
|
20
28
|
raise WebserverError, response.code unless response.code == 200
|
21
29
|
response
|
22
30
|
end
|
23
|
-
# Set the cookie header and instance variable
|
24
|
-
#
|
25
|
-
# @param cookie [String] The cookie text, as show in a 'set-cookie' header
|
26
|
-
def cookies cookie
|
27
|
-
@cookie = cookie
|
28
|
-
self.class.headers 'Cookie' => cookie
|
29
|
-
end
|
30
31
|
|
31
32
|
# Raises an error if we aren't currently logged in
|
32
33
|
#
|
33
34
|
def logged_in?
|
34
|
-
raise NotAuthenticated if @
|
35
|
+
raise NotAuthenticated if @cookies.nil? or @modhash.nil?
|
35
36
|
end
|
36
37
|
|
37
38
|
# Posts to '/api/friend'. This method exists because there are tons of things that use this
|
data/lib/snoo/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snoo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|
@@ -59,6 +59,9 @@ files:
|
|
59
59
|
- LICENSE
|
60
60
|
- README.md
|
61
61
|
- Rakefile
|
62
|
+
- examples/flairbot/README.md
|
63
|
+
- examples/flairbot/config.yaml
|
64
|
+
- examples/flairbot/flair.rb
|
62
65
|
- lib/snoo.rb
|
63
66
|
- lib/snoo/account.rb
|
64
67
|
- lib/snoo/exceptions.rb
|