libpocket.rb (3475B)
1 # frozen_string_literal: true 2 # https://getpocket.com/developer/docs/overview 3 require 'open-uri' 4 require 'json' 5 require 'net/http' 6 7 def die(message) 8 warn "error: #{message}" 9 exit 1 10 end 11 12 # The Pocket API authentication system 13 class PocketAuth 14 def initialize 15 die 'Please set the POCKET_CONSUMER_KEY environment variable.' unless ENV['POCKET_CONSUMER_KEY'] 16 @consumer_key = ENV['POCKET_CONSUMER_KEY'] 17 @base = 'https://getpocket.com/v3' 18 @headers = { 'Content-Type' => 'application/json; charset=UTF8', 'X-Accept' => 'application/json' } 19 @access_token = read_access_token 20 end 21 22 def credentials 23 { consumer_key: @consumer_key, access_token: @access_token } 24 end 25 26 private 27 28 ACCESS_TOKEN_FILE = "#{ENV['HOME']}/.cache/pocket-access-token" 29 30 def read_access_token 31 if File.exist? ACCESS_TOKEN_FILE 32 @access_token = File.read ACCESS_TOKEN_FILE 33 else 34 @access_token = request_access_token 35 puts "Writing access token to #{ACCESS_TOKEN_FILE}, remove this file to revoke authorization." 36 File.write ACCESS_TOKEN_FILE, @access_token 37 end 38 end 39 40 def generate_auth_uri 41 query = { consumer_key: @consumer_key, redirect_uri: 'https://example.com' } 42 response = Net::HTTP.post URI("#{@base}/oauth/request"), query.to_json, @headers 43 if response.code == '200' 44 @pocket_code = JSON.parse(response.body)['code'] 45 else 46 die 'Could not retrieve code.' 47 end 48 "https://getpocket.com/auth/authorize?request_token=#{@pocket_code}&redirect_uri=#{query['redirect_uri']}" 49 end 50 51 def authenticate 52 query = { consumer_key: @consumer_key, code: @pocket_code } 53 response = Net::HTTP.post URI("#{@base}/oauth/authorize"), query.to_json, @headers 54 return nil unless response.code == '200' 55 56 JSON.parse(response.body) 57 end 58 59 def request_access_token 60 puts "Please open: #{generate_auth_uri}" 61 # TODO: start a temp server, react when receive request (URL passed in redirect_uri above) 62 print 'Press enter when done.' 63 STDIN.getc 64 auth_response = authenticate 65 die 'Could not authenticate' unless auth_response 66 puts "Authenticated for username #{body['username']}" 67 @access_token = body['access_token'] 68 end 69 end 70 71 # The Pocket API interface class 72 class Pocket 73 def initialize 74 @base = 'https://getpocket.com/v3' 75 @headers = { 'Content-Type' => 'application/json; charset=UTF8', 'X-Accept' => 'application/json' } 76 @security_params = PocketAuth.new.credentials 77 end 78 79 def api_call(endpoint, params) 80 response = Net::HTTP.post URI(@base+endpoint), params.merge(@security_params).to_json, @headers 81 if response.code == '200' 82 block_given? ? (yield JSON.load(response.body)) : (return JSON.load(response.body)) 83 else 84 die "Could not add, code #{response.code}" 85 end 86 end 87 88 def archive_all_pocketbook 89 tagged_pocketbook = api_call('/get', { tag: 'pocketbook', state: :any }) 90 ids = tagged_pocketbook['list'].map(&:first) 91 actions = ids.map { |id| [{action: :tags_remove, item_id: id, tags: 'pocketbook'}, {action: :archive, item_id: id}]}.flatten 92 result = api_call('/send', { actions: }) 93 warn "Some errors, ids #{ids}" unless result['action_results'].all? 94 end 95 96 def save(url) 97 api_call("/add", url: url) 98 end 99 100 def retrieve_list(query) 101 # merge overwrites sort key if needed 102 api_call("/get", { sort: 'newest' }.merge(query)) do |response_body| 103 response_body['list'].map { |_id, data| data } 104 end 105 end 106 end 107