dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

commit dc1f3fb35476e2ffc2c3696a21ba2218f81399da
parent 9007730697e5a56e052d5c5091e7350d9ce20950
Author: Alex Balgavy <alex@balgavy.eu>
Date:   Tue, 24 Jan 2023 15:50:37 +0100

pocket: split out API interaction into separate file

Diffstat:
Ascripts/libpocket.rb | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/pocket | 124+++++++++----------------------------------------------------------------------
2 files changed, 112 insertions(+), 111 deletions(-)

diff --git a/scripts/libpocket.rb b/scripts/libpocket.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true +# https://getpocket.com/developer/docs/overview +require 'open-uri' +require 'json' +require 'net/http' + +def die(message) + warn "error: #{message}" + exit 1 +end + +# The Pocket API authentication system +class PocketAuth + def initialize + die 'Please set the POCKET_CONSUMER_KEY environment variable.' unless ENV['POCKET_CONSUMER_KEY'] + @consumer_key = ENV['POCKET_CONSUMER_KEY'] + @base = 'https://getpocket.com/v3' + @headers = { 'Content-Type' => 'application/json; charset=UTF8', 'X-Accept' => 'application/json' } + @access_token = read_access_token + end + + def credentials + { consumer_key: @consumer_key, access_token: @access_token } + end + + private + + ACCESS_TOKEN_FILE = "#{ENV['HOME']}/.cache/pocket-access-token" + + def read_access_token + if File.exist? ACCESS_TOKEN_FILE + @access_token = File.read ACCESS_TOKEN_FILE + else + @access_token = request_access_token + puts "Writing access token to #{ACCESS_TOKEN_FILE}, remove this file to revoke authorization." + File.write ACCESS_TOKEN_FILE, @access_token + end + end + + def generate_auth_uri + query = { consumer_key: @consumer_key, redirect_uri: 'https://example.com' } + response = Net::HTTP.post URI("#{@base}/oauth/request"), query.to_json, @headers + if response.code == '200' + @pocket_code = JSON.parse(response.body)['code'] + else + die 'Could not retrieve code.' + end + "https://getpocket.com/auth/authorize?request_token=#{@pocket_code}&redirect_uri=#{query['redirect_uri']}" + end + + def authenticate + query = { consumer_key: @consumer_key, code: @pocket_code } + response = Net::HTTP.post URI("#{@base}/oauth/authorize"), query.to_json, @headers + return nil unless response.code == '200' + + JSON.parse(response.body) + end + + def request_access_token + puts "Please open: #{generate_auth_uri}" + # TODO: start a temp server, react when receive request (URL passed in redirect_uri above) + print 'Press enter when done.' + STDIN.getc + auth_response = authenticate + die 'Could not authenticate' unless auth_response + puts "Authenticated for username #{body['username']}" + @access_token = body['access_token'] + end +end + +# The Pocket API interface class +class Pocket + def initialize + @base = 'https://getpocket.com/v3' + @headers = { 'Content-Type' => 'application/json; charset=UTF8', 'X-Accept' => 'application/json' } + @security_params = PocketAuth.new.credentials + end + + def api_call(endpoint, params) + response = Net::HTTP.post URI(@base+endpoint), params.merge(@security_params).to_json, @headers + if response.code == '200' + block_given? ? (yield JSON.load(response.body)) : (return JSON.load(response.body)) + else + die "Could not add, code #{response.code}" + end + end + + def save(url) + api_call("/add", url: url) + end + + def retrieve_list(query) + # merge overwrites sort key if needed + api_call("/get", { sort: 'newest' }.merge(query)) do |response_body| + response_body['list'].map { |_id, data| data } + end + end +end + diff --git a/scripts/pocket b/scripts/pocket @@ -1,109 +1,5 @@ #!/usr/bin/env ruby -# frozen_string_literal: true - -require 'open-uri' -require 'json' -require 'net/http' - -def die(message) - warn "error: #{message}" - exit 1 -end - -# The Pocket API authentication system -class PocketAuth - def initialize - die 'Please set the POCKET_CONSUMER_KEY environment variable.' unless ENV['POCKET_CONSUMER_KEY'] - @consumer_key = ENV['POCKET_CONSUMER_KEY'] - @base = 'https://getpocket.com/v3' - @headers = { 'Content-Type' => 'application/json; charset=UTF8', 'X-Accept' => 'application/json' } - @access_token = read_access_token - end - - def credentials - { consumer_key: @consumer_key, access_token: @access_token } - end - - private - - ACCESS_TOKEN_FILE = "#{ENV['HOME']}/.cache/pocket-access-token" - - def read_access_token - if File.exist? ACCESS_TOKEN_FILE - @access_token = File.read ACCESS_TOKEN_FILE - else - @access_token = request_access_token - puts "Writing access token to #{ACCESS_TOKEN_FILE}, remove this file to revoke authorization." - File.write ACCESS_TOKEN_FILE, @access_token - end - end - - def generate_auth_uri - query = { consumer_key: @consumer_key, redirect_uri: 'https://google.com' } - response = Net::HTTP.post URI("#{@base}/oauth/request"), query.to_json, @headers - if response.code == '200' - @pocket_code = JSON.parse(response.body)['code'] - else - die 'Could not retrieve code.' - end - "https://getpocket.com/auth/authorize?request_token=#{@pocket_code}&redirect_uri=#{query['redirect_uri']}" - end - - def authenticate - query = { consumer_key: @consumer_key, code: @pocket_code } - response = Net::HTTP.post URI("#{@base}/oauth/authorize"), query.to_json, @headers - return nil unless response.code == '200' - - JSON.parse(response.body) - end - - def request_access_token - puts "Please open: #{generate_auth_uri}" - # TODO: start a temp server, react when receive request (URL passed in redirect_uri above) - print 'Press enter when done.' - STDIN.getc - auth_response = authenticate - die 'Could not authenticate' unless auth_response - puts "Authenticated for username #{body['username']}" - @access_token = body['access_token'] - end -end - -# The Pocket API interface class -class Pocket - def initialize - @base = 'https://getpocket.com/v3' - @headers = { 'Content-Type' => 'application/json; charset=UTF8', 'X-Accept' => 'application/json' } - @security_params = PocketAuth.new.credentials - end - - def api_call(url_string, params) - response = Net::HTTP.post URI(url_string), params.merge(@security_params).to_json, @headers - if response.code == '200' - yield response if block_given? - else - die "Could not add, code #{response.code}" - end - end - - def save(url) - api_call("#{@base}/add", url: url) - end - - def retrieve_list(query) - # merge overwrites sort key if needed - api_call("#{@base}/get", { sort: 'newest' }.merge(query)) { |resp| parse_article_list(resp.body).join("\n") } - end - - private - - def parse_article_list(response_body) - JSON.parse(response_body)['list'].inject([]) do |lines, (_id, data)| - lines << "#{data['resolved_title']}\t#{data['resolved_url']}" - end - end -end - +require_relative './libpocket.rb' def usage puts <<~HEREDOC Usage: pocket command [arg1 [arg2...]] @@ -132,6 +28,12 @@ def get_count_sort(args) { count: count.abs } end +def article_list_to_s(article_list) + article_list.to_a.inject([]) do |lines, article| + lines << "#{article['resolved_title']}\t#{article['resolved_url']}" + end.join("\n") +end + pocket = Pocket.new if ARGV[0] == 'save' @@ -145,22 +47,22 @@ if ARGV[0] == 'save' end elsif ARGV[0].start_with? 'audio' params = { domain: '*.bandcamp.com', contentType: 'article' }.merge(get_count_sort(ARGV)) - print pocket.retrieve_list(params) + print article_list_to_s(pocket.retrieve_list(params)) elsif ARGV[0].start_with? 'video' params = { contentType: 'video' }.merge(get_count_sort(ARGV)) - print pocket.retrieve_list(params) + print article_list_to_s(pocket.retrieve_list(params)) elsif ARGV[0].start_with? 'article' if ARGV[1] params = { contentType: 'article' }.merge(get_count_sort(ARGV)) - print pocket.retrieve_list(params) + print article_list_to_s(pocket.retrieve_list(params)) else - print pocket.retrieve_list(contentType: 'article') + print article_list_to_s(pocket.retrieve_list(contentType: 'article')) end elsif ARGV[0].start_with? 'list' if ARGV[1] - print pocket.retrieve_list(get_count_sort(ARGV)) + print article_list_to_s(pocket.retrieve_list(get_count_sort(ARGV))) else - print pocket.retrieve_list({}) + print article_list_to_s(pocket.retrieve_list({})) end else die "Command '#{ARGV[0]}' not recognised."