Class: RedditBot::Bot
- Inherits:
-
Object
- Object
- RedditBot::Bot
- Defined in:
- lib/reddit_bot.rb
Constant Summary collapse
- @@skip_erroneous_descending_ids =
lambda do |array| array.reverse.each_with_object([]) do |item, result| unless result.empty? a, b = [item["id"], result.first["id"]] next if a == b || a.size < b.size || !(a.size > b.size) && a < b end result.unshift item end end
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Instance Method Summary collapse
- #each_comment_of_the_post_thread(article) ⇒ Object
- #each_new_post_with_top_level_comments ⇒ Object
-
#initialize(secrets, **kwargs) ⇒ Bot
constructor
A new instance of Bot.
- #json(mtd, path, _form = []) ⇒ Object
- #leave_a_comment(thing_id, text) ⇒ Object
-
#new_posts(subreddit = nil, caching = false) ⇒ Object
:yields: JSON objects: [“data”] part of post or self.post.
-
#report(reason, thing_id) ⇒ Object
# [subreddit]
Stringsubreddit name without “/r” prefix # [page]Stringpage name without “/wiki/” prefix # [text] :nodoc: def wiki_edit subreddit, page, text puts “editing wiki page ‘/r/#subreddit/wiki/#page’” json :post, “/r/#subreddit/api/wiki/edit”, page: page, content: text # [“previous”, result[“children“].last], end. - #set_post_flair(post, link_flair_css_class, link_flair_text) ⇒ Object
- #subreddit_iterate(what, **kwargs) ⇒ Object
Constructor Details
#initialize(secrets, **kwargs) ⇒ Bot
18 19 20 21 |
# File 'lib/reddit_bot.rb', line 18 def initialize secrets, **kwargs @name, @secret_password, @user_agent, *@secret_auth = secrets.values_at *i{ login password user_agent client_id client_secret } @subreddit = kwargs[:subreddit] end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
16 17 18 |
# File 'lib/reddit_bot.rb', line 16 def name @name end |
Instance Method Details
#each_comment_of_the_post_thread(article) ⇒ Object
163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/reddit_bot.rb', line 163 def each_comment_of_the_post_thread article Enumerator.new do |e| f = lambda do |smth| smth["data"]["children"].each do |child| f[child["data"]["replies"]] if child["data"]["replies"].is_a? Hash fail "unknown type child['kind']: #{child["kind"]}" unless child["kind"] == "t1" e << [child["data"]["name"], child["data"]] end end f[ json(:get, "/comments/#{article}", depth: 100500, limit: 100500).tap do |t| fail "smth weird about /comments/<id> response" unless t.size == 2 end[1] ] end end |
#each_new_post_with_top_level_comments ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/reddit_bot.rb', line 150 def each_new_post_with_top_level_comments # TODO add keys assertion like in method above? json(:get, "/r/#{@subreddit}/new")["data"]["children"].each do |post| fail "unknown type post['kind']: #{post["kind"]}" unless post["kind"] == "t3" t = json :get, "/comments/#{post["data"]["id"]}", depth: 1, limit: 100500#, sort: "top" fail "smth weird about /comments/<id> response" unless t.size == 2 yield post["data"], t[1]["data"]["children"].map{ |child| fail "unknown type child['kind']: #{child["kind"]}" unless child["kind"] == "t1" child["data"] }.to_enum end end |
#json(mtd, path, _form = []) ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/reddit_bot.rb', line 23 def json mtd, path, _form = [] form = _form.to_h response = begin JSON.load resp_with_token mtd, path, form.merge({api_type: "json"}) rescue JSON::ParserError $!..slice! 1000..-1 raise end return response unless response.is_a?(Hash) && response["json"] && !response["json"]["errors"].empty? # for example, flairlist.json and {"error": 403} do not have it Module.nesting[1].logger.error "ERROR OCCURED on #{[mtd, path]}" fail "unknown how to handle multiple errors" if 1 < response["json"]["errors"].size Module.nesting[1].logger.error "error: #{response["json"]["errors"]}" error, description = response["json"]["errors"].first case error when "ALREADY_SUB" ; Module.nesting[1].logger.warn "was rejected by moderator if you didn't see in dups" when "RATELIMIT" fail error unless description[/\Ayou are doing that too much\. try again in (\d) minutes\.\z/] Module.nesting[1].logger.info "retrying in #{$1.to_i + 1} minutes" sleep ($1.to_i + 1) * 60 return json mtd, path, _form else ; fail error end end |
#leave_a_comment(thing_id, text) ⇒ Object
82 83 84 85 86 87 88 89 90 |
# File 'lib/reddit_bot.rb', line 82 def leave_a_comment thing_id, text Module.nesting[1].logger.warn "leaving a comment on '#{thing_id}'" json(:post, "/api/comment", thing_id: thing_id, text: text, ).tap do |result| fail result["json"]["errors"].to_s unless result["json"]["errors"].empty? end end |
#new_posts(subreddit = nil, caching = false) ⇒ Object
:yields: JSON objects: [“data”] part of post or self.post
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 |
# File 'lib/reddit_bot.rb', line 112 def new_posts subreddit = nil, caching = false cache = lambda do |id, &block| next block.call unless caching require "fileutils" FileUtils.mkdir_p "cache" filename = "cache/#{Digest::MD5.hexdigest id.inspect}" next YAML.load File.read filename if File.exist? filename block.call.tap do |data| File.write filename, YAML.dump(data) end end Enumerator.new do |e| after = {} loop do # TODO maybe force lib user to prepend "r/" to @subreddit constructor? args = [:get, "/#{subreddit || (@subreddit ? "r/#{@subreddit}" : fail)}/new", {limit: 100}.merge(after)] result = cache.call(args){ json *args } fail if result.keys != %w{ kind data } fail if result["kind"] != "Listing" fail result["data"].keys.inspect unless [ %w{ after dist modhash whitelist_status children before }, %w{ modhash dist children after before }, %w{ after dist modhash geo_filter children before }, ].include? result["data"].keys @@skip_erroneous_descending_ids[ result["data"]["children"].map do |post| fail "unknown type post['kind']: #{post["kind"]}" unless post["kind"] == "t3" post["data"].dup.tap do |data| data["url"] = "https://www.reddit.com" + data["url"] if /\A\/r\/[0-9a-zA-Z_]+\/comments\/[0-9a-z]{5,6}\// =~ data["url"] if data["crosspost_parent"] end end ].each do |data| e << data end break unless marker = result["data"]["after"] after = {after: marker} end end end |
#report(reason, thing_id) ⇒ Object
# [subreddit] String subreddit name without “/r” prefix # [page] String page name without “/wiki/” prefix # [text] :nodoc: def wiki_edit subreddit, page, text
puts "editing wiki page '/r/#{subreddit}/wiki/#{page}'"
json :post,
"/r/#{subreddit}/api/wiki/edit",
page: page,
content: text
# ["previous", result["data"]["children"].last["id"]],
end
60 61 62 63 64 65 66 |
# File 'lib/reddit_bot.rb', line 60 def report reason, thing_id Module.nesting[1].logger.warn "reporting '#{thing_id}'" json :post, "/api/report", reason: "other", other_reason: reason, thing_id: thing_id end |
#set_post_flair(post, link_flair_css_class, link_flair_text) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/reddit_bot.rb', line 68 def set_post_flair post, link_flair_css_class, link_flair_text Module.nesting[1].logger.warn "setting flair '#{link_flair_css_class}' with text '#{link_flair_text}' to post '#{post["name"]}'" if {"error"=>403} == @flairselector_choices ||= json(:post, "/r/#{@subreddit}/api/flairselector", link: post["name"]) Module.nesting[1].logger.error "possibly not enough permissions for /r/#{@subreddit}/api/flairselector" return end json :post, "/api/selectflair", link: post["name"], text: link_flair_text, flair_template_id: @flairselector_choices["choices"].find{ |i| i["flair_css_class"] == link_flair_css_class }.tap{ |flair| fail "can't find '#{link_flair_css_class}' flair class at https://www.reddit.com/r/#{@subreddit}/about/flair/#link_templates" unless flair }["flair_template_id"] end |
#subreddit_iterate(what, **kwargs) ⇒ Object
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/reddit_bot.rb', line 178 def subreddit_iterate what, **kwargs Enumerator.new do |e| after = {} loop do break unless marker = json(:get, "/r/#{@subreddit}/#{what}", {limit: 100}.merge(after).merge(kwargs)).tap do |result| fail if %w{ kind data } != result.keys fail if "Listing" != result["kind"] fail result["data"].keys.inspect unless result["data"].keys == %w{ after dist modhash whitelist_status children before } || result["data"].keys == %w{ modhash dist children after before } result["data"]["children"].each do |post| fail "unknown type post['kind']: #{post["kind"]}" unless post["kind"] == "t3" e << ( post["data"].tap do |data| data["url"] = "https://www.reddit.com" + data["url"] if /\A\/r\/[0-9a-zA-Z_]+\/comments\/[0-9a-z]{5,6}\// =~ data["url"] if data["crosspost_parent"] end ) end end["data"]["after"] after = {after: marker} end end end |