reddit_bot 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 15d553095ba152bbaae221eae90f979af34d3c15
4
+ data.tar.gz: e69581752893dd5e33295288e41913d8c8911b24
5
+ SHA512:
6
+ metadata.gz: c84e63458312902936b0bd272ff4af284f6277b601b12dee1b3e65471e52809679b00a9d179d5e427c2757d61ab8280ce8544f261507788679afd94a1a76a7c1
7
+ data.tar.gz: fdba28df65eb98e47a93397e4115dc30dd7b01adbd59b44e885a65e8f2b537161089dd0fa8c349f9b74b860eea3ddd9511cd6f1ef8e9dd2a509bf84f58bb2cdd
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Nakilon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/lib/reddit_bot.rb ADDED
@@ -0,0 +1,196 @@
1
+ STDOUT.sync = true
2
+
3
+ require "pp"
4
+
5
+ require "net/http"
6
+ require "openssl"
7
+ require "json"
8
+
9
+
10
+ module RedditBot
11
+ VERSION = "0.1.0"
12
+
13
+ class << self
14
+
15
+ attr_accessor :token_cached
16
+ attr_accessor :username
17
+ attr_accessor :iden_and_captcha
18
+ attr_accessor :ignore_captcha
19
+ attr_accessor :secrets
20
+
21
+ def init *secrets, **kwargs
22
+ @secrets = secrets
23
+ kwargs
24
+ @ignore_captcha = kwargs[:ignore_captcha]
25
+ end
26
+
27
+ def json mtd, url, form = []
28
+ response = JSON.parse resp_with_token mtd, url, Hash[form].merge({api_type: "json"})
29
+ if response.is_a?(Hash) && response["json"] # for example, flairlist.json and {"error": 403} do not have it
30
+ puts "ERROR OCCURED on #{[mtd, url]}" unless response["json"]["errors"].empty?
31
+ # pp response["json"]
32
+ response["json"]["errors"].each do |error, description|
33
+ puts "error: #{[error, description]}"
34
+ case error
35
+ when "ALREADY_SUB" ; puts "was rejected by moderator if you didn't see in dups"
36
+ when "BAD_CAPTCHA" ; update_captcha
37
+ json mtd, url, form.concat([
38
+ ["iden", @iden_and_captcha[0]],
39
+ ["captcha", @iden_and_captcha[1]],
40
+ ]) unless @ignore_captcha
41
+ else ; raise error
42
+ end
43
+ end
44
+ end
45
+ response
46
+ end
47
+
48
+ def wiki_edit subreddit, page, text
49
+ json :post,
50
+ "/r/#{subreddit}/api/wiki/edit",
51
+ [
52
+ ["page", page],
53
+ ["content", text]
54
+ ]
55
+ # ["previous", result["data"]["children"].last["id"]],
56
+ end
57
+
58
+ private
59
+
60
+ def token
61
+ return @token_cached if @token_cached
62
+ response = JSON.parse(reddit_resp(:post,
63
+ "https://www.reddit.com/api/v1/access_token",
64
+ [
65
+ ["grant_type", "password"],
66
+ ["username", @username = @secrets[3]],
67
+ ["password", @secrets[2]],
68
+ ],
69
+ {}, # headers
70
+ [@secrets[0], @secrets[1]],
71
+ ))
72
+ raise response.inspect unless @token_cached = response["access_token"]
73
+ puts "new token is: #{@token_cached}"
74
+ update_captcha if "true" == resp_with_token(:get, "/api/needs_captcha", {})
75
+ @token_cached
76
+ end
77
+
78
+ def update_captcha
79
+ return if @ignore_captcha
80
+ pp iden_json = json(:post, "/api/new_captcha")
81
+ iden = iden_json["json"]["data"]["iden"]
82
+ # return @iden_and_captcha = [iden, "\n"] if @ignore_captcha
83
+ # pp resp_with_token(:get, "/captcha/#{iden_json["json"]["data"]["iden"]}", {})
84
+ puts "CAPTCHA: https://reddit.com/captcha/#{iden}"
85
+ @iden_and_captcha = [iden, gets.strip]
86
+ end
87
+
88
+ def resp_with_token mtd, url, form
89
+ {} until _ = catch(:"401") do
90
+ reddit_resp mtd, "https://oauth.reddit.com" + url, form, [
91
+ ["Authorization", "bearer #{token}"],
92
+ ["User-Agent", "bot/#{@username}/0.0.0 by /u/nakilon"],
93
+ ], nil # base auth
94
+ end
95
+ _
96
+ end
97
+
98
+ def reddit_resp *args
99
+ response = nil
100
+ 1.times do
101
+ response = _resp *args
102
+ case response.code
103
+ when "502", "503", "520", "500", "521", "504", "400", "522"
104
+ puts "LOL #{response.code} at #{Time.now}?"
105
+ pp args
106
+ sleep 5
107
+ redo
108
+ when "409"
109
+ puts "Conflict (409)? at #{Time.now}?"
110
+ pp args
111
+ sleep 5
112
+ redo
113
+ when "401"
114
+ puts "probably token is expired (401): #{response.body}"
115
+ sleep 5
116
+ # init *@secrets
117
+ @token_cached = nil # maybe just update_captcha?
118
+ throw :"401"
119
+ when "403"
120
+ puts "access denied: #{response.body}"
121
+ sleep 5
122
+ # throw :"403"
123
+ when "200"
124
+ "ok"
125
+ else
126
+ # puts response.body if response.code == "400"
127
+ # fail "#{response.code} at '#{args[1]}'"
128
+ fail "#{response.code} for '#{args}'"
129
+ end
130
+ end
131
+ response.body
132
+ end
133
+
134
+ def _resp mtd, url, form, headers, base_auth
135
+ uri = URI.parse url
136
+ request = if mtd == :get
137
+ uri.query = URI.encode_www_form form # wtf OpenSSL::SSL::SSLError
138
+ Net::HTTP::Get.new(uri)
139
+ else
140
+ Net::HTTP::Post.new(uri).tap{ |r| r.set_form_data form }
141
+ end
142
+ request.basic_auth *base_auth if base_auth
143
+ headers.each{ |k, v| request[k] = v }
144
+ # puts request.path
145
+ # pp request.to_hash
146
+ # puts request.body
147
+ http = begin # I hope this doesn't need retry (Get|Post).new
148
+ Net::HTTP.start uri.host,
149
+ use_ssl: uri.scheme == "https",
150
+ verify_mode: OpenSSL::SSL::VERIFY_NONE,
151
+ open_timeout: 300
152
+ rescue Errno::ECONNRESET
153
+ puts "ERROR: SSL_connect (Errno::ECONNRESET)"
154
+ sleep 5
155
+ retry
156
+ rescue OpenSSL::SSL::SSLError
157
+ puts "ERROR: SSL_connect SYSCALL returned=5 errno=0 state=SSLv3 read server session ticket A (OpenSSL::SSL::SSLError)"
158
+ sleep 5
159
+ retry
160
+ rescue Net::OpenTimeout
161
+ puts "ERROR: execution expired (Net::OpenTimeout)"
162
+ sleep 5
163
+ retry
164
+ end
165
+ response = begin
166
+ http.request request
167
+ rescue Net::ReadTimeout, Errno::EPIPE # doubt in Errno::EPIPE
168
+ puts "ERROR: Net::ReadTimeout"
169
+ retry
170
+ end
171
+ puts %w{
172
+ x-ratelimit-remaining
173
+ x-ratelimit-used
174
+ x-ratelimit-reset
175
+ }.map{ |key| "#{key}=#{response.to_hash[key]}" }.join ", " \
176
+ if ENV["LOGNAME"] == "nakilon"
177
+ # if response.to_hash["x-ratelimit-remaining"]
178
+ # p response.to_hash["x-ratelimit-remaining"][0]
179
+ # fail response.to_hash["x-ratelimit-remaining"][0]
180
+ # end
181
+ fail response.to_hash["x-ratelimit-remaining"][0] \
182
+ if response.to_hash["x-ratelimit-remaining"] &&
183
+ response.to_hash["x-ratelimit-remaining"][0].size <= 2
184
+ # /home/ec2-user/largeimages/bot.rb:126:in `_resp': 288 (RuntimeError)
185
+ # File.write File.join(__dir__(), "temp.json"), response.body
186
+ # if response.code == "401"
187
+ # puts request.path
188
+ # puts request.body
189
+ # pp request.to_hash
190
+ # end
191
+ response
192
+ end
193
+
194
+ end
195
+
196
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "reddit_bot"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reddit_bot"
8
+ spec.version = RedditBot::VERSION
9
+ spec.authors = ["Victor Maslov"]
10
+ spec.email = ["nakilon@gmail.com"]
11
+
12
+ spec.summary = "Library for Reddit bots"
13
+ spec.description = ""
14
+ spec.homepage = ""
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")#.reject { |f| f.match(%r{^(test|spec|features)/})/ }
18
+ # spec.require_paths = ["lib"]
19
+
20
+ # spec.add_development_dependency "bundler", "~> 1.11"
21
+ # spec.add_development_dependency "rake", "~> 10.0"
22
+ # spec.add_development_dependency "rspec", "~> 3.0"
23
+ end
24
+
25
+ # spec.test_files = ["spec/"]
26
+ # spec.required_ruby_version = ">= 2.0.0"
27
+ # spec.post_install_message = ""
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reddit_bot
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Victor Maslov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-05 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: ''
14
+ email:
15
+ - nakilon@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - Gemfile
21
+ - LICENSE.txt
22
+ - lib/reddit_bot.rb
23
+ - reddit_bot.gemspec
24
+ homepage: ''
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.0.14
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Library for Reddit bots
48
+ test_files: []