minisky 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a753ae5a6f7d2dd3089ee9b6a637e578051261c615fedd28a44f63ac1774b4d
4
- data.tar.gz: 479240c065cb3ba705aa4a9d07efa8c4904ec2aca2e6ed0e847d391ab8cd62fe
3
+ metadata.gz: b22e3b707a428f0381e5c194ce6b5bd4118f6b6dfb7f2d249140c8025e3b1dd7
4
+ data.tar.gz: 2a6370ea6cb538c680252836295c411ff4c6df93b8c5aab9d8b73b843753febb
5
5
  SHA512:
6
- metadata.gz: f87814325757d1b19cee4e403d6e13afbf84b1353b932eedbce23faa6cb1abfe772d0cf2c459b21bb45ea08fa34173457dea3fdb266f32f1f4334e2e90d22692
7
- data.tar.gz: c4bea17a09982db0462e15e092fdf43c454fd94163d04099b119104fde555387621c5c1b8d51e1a1247ad2cece0b5fd9258b1c98555b0ffb7eca4b96107dd83a
6
+ metadata.gz: 3f86def9480e86d2989a52f143eacee8e5b78fede45bd6ba49ea7ad54f1da88a55555421d3fdfdddad025bed21a708c830e5e9b2c351ba57e016234e200b9f74
7
+ data.tar.gz: f8f7faa8d7e5df7807d6e9bb7ee1b87d1d5ab5503c0193fd031190be6e6346004618c66efdba96970fc93117d4f587c53ff5f17585a2ffa3ddafb503abc5a3cc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.4.0] - 2024-03-31 🐣
2
+
3
+ * allow passing non-JSON body to requests (e.g. when uploading blobs)
4
+ * allow passing custom headers to requests, including overriding `Content-Type`
5
+ * fixed error when the response is success but not JSON (e.g. an empty body like in deleteRecord)
6
+ * allow passing options to the client in the initializer
7
+ * aliased `default_progress` setting as `progress`
8
+ * added `base64` dependency explicitly to the gemspec - fixes a warning in Ruby 3.3, since it will be extracted as an optional gem in 3.4
9
+
1
10
  ## [0.3.1] - 2023-10-10
2
11
 
3
12
  * fixed Minisky not working on Ruby 2.x
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Example: print 10 latest posts from the user's home feed.
4
+ #
5
+ # Instead of using a config file to read & store authentication info, this example uses a customized client class
6
+ # which reads the password from the console and creates a throwaway access token.
7
+ #
8
+ # This approach makes sense for one-off scripts, but it shouldn't be used for things that need to be done repeatedly
9
+ # and often (the authentication-related endpoints have lower rate limits than others).
10
+
11
+ # load minisky from a local folder - you normally won't need this
12
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
13
+
14
+ require 'io/console'
15
+ require 'minisky'
16
+
17
+ class TransientClient
18
+ include Minisky::Requests
19
+
20
+ attr_reader :config, :host
21
+
22
+ def initialize(host, user)
23
+ @host = host
24
+ @config = { 'id' => user.gsub(/^@/, '') }
25
+ end
26
+
27
+ def ask_for_password
28
+ print "Enter password for @#{config['id']}: "
29
+ @config['pass'] = STDIN.noecho(&:gets).chomp
30
+ puts
31
+ end
32
+
33
+ def save_config
34
+ # ignore
35
+ end
36
+ end
37
+
38
+ host, handle = ARGV
39
+
40
+ unless host && handle
41
+ puts "Usage: #{$PROGRAM_NAME} <pds_hostname> <handle>"
42
+ exit 1
43
+ end
44
+
45
+ # create a client instance & read password
46
+ bsky = TransientClient.new(host, handle)
47
+ bsky.ask_for_password
48
+
49
+ # fetch 10 posts from the user's home feed
50
+ result = bsky.get_request('app.bsky.feed.getTimeline', { limit: 10 })
51
+
52
+ result['feed'].each do |r|
53
+ reason = r['reason']
54
+ reply = r['reply']
55
+ post = r['post']
56
+
57
+ if reason && reason['$type'] == 'app.bsky.feed.defs#reasonRepost'
58
+ puts "[Reposted by @#{reason['by']['handle']}]"
59
+ end
60
+
61
+ handle = post['author']['handle']
62
+ timestamp = Time.parse(post['record']['createdAt']).getlocal
63
+
64
+ puts "@#{handle} • #{timestamp}"
65
+ puts
66
+
67
+ if reply
68
+ puts "[in reply to @#{reply['parent']['author']['handle']}]"
69
+ puts
70
+ end
71
+
72
+ puts post['record']['text']
73
+ puts
74
+ puts "=" * 120
75
+ puts
76
+ end
@@ -3,7 +3,7 @@ require 'yaml'
3
3
  class Minisky
4
4
  attr_reader :host, :config
5
5
 
6
- def initialize(host, config_file)
6
+ def initialize(host, config_file, options = {})
7
7
  @host = host
8
8
  @config_file = config_file
9
9
 
@@ -18,6 +18,12 @@ class Minisky
18
18
  @send_auth_headers = false
19
19
  @auto_manage_tokens = false
20
20
  end
21
+
22
+ if options
23
+ options.each do |k, v|
24
+ self.send("#{k}=", v)
25
+ end
26
+ end
21
27
  end
22
28
 
23
29
  def save_config
@@ -39,6 +39,9 @@ class Minisky
39
39
  instance_variable_defined?('@auto_manage_tokens') ? @auto_manage_tokens : true
40
40
  end
41
41
 
42
+ alias progress default_progress
43
+ alias progress= default_progress=
44
+
42
45
  def base_url
43
46
  @base_url ||= "https://#{host}/xrpc"
44
47
  end
@@ -47,10 +50,10 @@ class Minisky
47
50
  @user ||= User.new(config)
48
51
  end
49
52
 
50
- def get_request(method, params = nil, auth: default_auth_mode)
53
+ def get_request(method, params = nil, auth: default_auth_mode, headers: nil)
51
54
  check_access if auto_manage_tokens && auth == true
52
55
 
53
- headers = authentication_header(auth)
56
+ headers = authentication_header(auth).merge(headers || {})
54
57
  url = URI("#{base_url}/#{method}")
55
58
 
56
59
  if params && !params.empty?
@@ -63,18 +66,24 @@ class Minisky
63
66
  handle_response(response)
64
67
  end
65
68
 
66
- def post_request(method, params = nil, auth: default_auth_mode)
69
+ def post_request(method, params = nil, auth: default_auth_mode, headers: nil)
67
70
  check_access if auto_manage_tokens && auth == true
68
71
 
69
- headers = authentication_header(auth).merge({ "Content-Type" => "application/json" })
70
- body = params ? params.to_json : ''
72
+ headers = authentication_header(auth).merge(headers || {})
73
+ headers["Content-Type"] = "application/json" unless headers.keys.any? { |k| k.to_s.downcase == 'content-type' }
74
+
75
+ body = if params.is_a?(String) || params.nil?
76
+ params.to_s
77
+ else
78
+ params.to_json
79
+ end
71
80
 
72
81
  response = Net::HTTP.post(URI("#{base_url}/#{method}"), body, headers)
73
82
  handle_response(response)
74
83
  end
75
84
 
76
85
  def fetch_all(method, params = nil, field:,
77
- auth: default_auth_mode, break_when: nil, max_pages: nil, progress: @default_progress)
86
+ auth: default_auth_mode, break_when: nil, max_pages: nil, headers: nil, progress: @default_progress)
78
87
  data = []
79
88
  params = {} if params.nil?
80
89
  pages = 0
@@ -82,7 +91,7 @@ class Minisky
82
91
  loop do
83
92
  print(progress) if progress
84
93
 
85
- response = get_request(method, params, auth: auth)
94
+ response = get_request(method, params, auth: auth, headers: headers)
86
95
  records = response[field]
87
96
  cursor = response['cursor']
88
97
 
@@ -152,14 +161,14 @@ class Minisky
152
161
  alias_method :do_post_request, :post_request
153
162
  private :do_get_request, :do_post_request
154
163
 
155
- def get_request(method, params = nil, auth: default_auth_mode, **kwargs)
164
+ def get_request(method, params = nil, auth: default_auth_mode, headers: nil, **kwargs)
156
165
  params ||= kwargs unless kwargs.empty?
157
- do_get_request(method, params, auth: auth)
166
+ do_get_request(method, params, auth: auth, headers: headers)
158
167
  end
159
168
 
160
- def post_request(method, params = nil, auth: default_auth_mode, **kwargs)
169
+ def post_request(method, params = nil, auth: default_auth_mode, headers: nil, **kwargs)
161
170
  params ||= kwargs unless kwargs.empty?
162
- do_post_request(method, params, auth: auth)
171
+ do_post_request(method, params, auth: auth, headers: headers)
163
172
  end
164
173
  end
165
174
 
@@ -210,16 +219,15 @@ class Minisky
210
219
  def handle_response(response)
211
220
  status = response.code.to_i
212
221
  message = response.message
222
+ response_body = (response.content_type == 'application/json') ? JSON.parse(response.body) : response.body
213
223
 
214
224
  case response
215
225
  when Net::HTTPSuccess
216
- JSON.parse(response.body)
226
+ response_body
217
227
  when Net::HTTPRedirection
218
228
  raise UnexpectedRedirect.new(status, message, response['location'])
219
229
  else
220
- data = (response.content_type == 'application/json') ? JSON.parse(response.body) : response.body
221
-
222
- error_class = if data.is_a?(Hash) && data['error'] == 'ExpiredToken'
230
+ error_class = if response_body.is_a?(Hash) && response_body['error'] == 'ExpiredToken'
223
231
  ExpiredTokenError
224
232
  elsif response.is_a?(Net::HTTPClientError)
225
233
  ClientErrorResponse
@@ -229,7 +237,7 @@ class Minisky
229
237
  BadResponse
230
238
  end
231
239
 
232
- raise error_class.new(status, message, data)
240
+ raise error_class.new(status, message, response_body)
233
241
  end
234
242
  end
235
243
  end
@@ -1,5 +1,5 @@
1
1
  require_relative 'minisky'
2
2
 
3
3
  class Minisky
4
- VERSION = "0.3.1"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minisky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-10 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-03-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base64
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
13
27
  description: A very simple client class that lets you log in to the Bluesky API and
14
28
  make any requests there.
15
29
  email:
@@ -21,6 +35,7 @@ files:
21
35
  - CHANGELOG.md
22
36
  - LICENSE.txt
23
37
  - README.md
38
+ - example/ask_password.rb
24
39
  - example/fetch_my_posts.rb
25
40
  - example/fetch_profile.rb
26
41
  - example/post_skeet.rb