bskyrb 0.5 → 0.5.2

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.
@@ -1,3 +1,4 @@
1
+ # typed: true
1
2
  module Bskyrb
2
3
  class RecordManager
3
4
  include RequestUtils
@@ -9,6 +10,7 @@ module Bskyrb
9
10
 
10
11
  def get_post_by_url(url, depth = 10)
11
12
  # e.g. "https://staging.bsky.app/profile/naia.bsky.social/post/3jszsrnruws27"
13
+ # or "at://did:plc:scx5mrfxxrqlfzkjcpbt3xfr/app.bsky.feed.post/3jszsrnruws27"
12
14
  # regex by chatgpt:
13
15
  query = Bskyrb::AppBskyFeedGetpostthread::GetPostThread::Input.new.tap do |q|
14
16
  q.uri = at_post_link(session.pds, url)
@@ -16,7 +18,7 @@ module Bskyrb
16
18
  end
17
19
  res = HTTParty.get(
18
20
  get_post_thread_uri(session.pds, query),
19
- headers: default_authenticated_headers(session)
21
+ headers: default_authenticated_headers(session),
20
22
  )
21
23
  Bskyrb::AppBskyFeedDefs::PostView.from_hash res["thread"]["post"]
22
24
  end
@@ -27,7 +29,7 @@ module Bskyrb
27
29
  HTTParty.post(
28
30
  upload_blob_uri(session.pds),
29
31
  body: image_bytes,
30
- headers: default_authenticated_headers(session)
32
+ headers: default_authenticated_headers(session),
31
33
  )
32
34
  end
33
35
 
@@ -38,47 +40,17 @@ module Bskyrb
38
40
  HTTParty.post(
39
41
  create_record_uri(session.pds),
40
42
  body: input.to_h.compact.to_json,
41
- headers: default_authenticated_headers(session)
43
+ headers: default_authenticated_headers(session),
42
44
  )
43
45
  end
44
46
 
45
- def detect_facets(json_hash) # TODO, DOES NOT WORK YET
46
- # For some reason this always fails at finding text records and I have no idea why
47
- # Detect domain names that have been @mentioned in the text
48
- matches = json_hash["record"]["text"].scan(/@([^\s.]+\.[^\s]+)/)
49
-
50
- # Create a facets array to hold the resolved handles
51
- facets = []
52
-
53
- # Loop through the matches and resolve the handles
54
- matches.each do |match|
55
- handle = match[0]
56
- resolved_handle = resolve_handle(session.pds, handle)
57
- byte_start = json_hash["record"]["text"].index("@" + handle)
58
- byte_end = byte_start + handle.length
59
- facet = {
60
- "$type": "app.bsky.richtext.facet",
61
- features: [
62
- {
63
- "$type": "app.bsky.richtext.facet#mention",
64
- did: resolved_handle
65
- }
66
- ],
67
- index: {
68
- byteStart: byte_start,
69
- byteEnd: byte_end
70
- }
71
- }
72
- facets.push(facet)
73
- end
74
-
75
- # Append the facets to the JSON hash
76
- json_hash["record"]["facets"] = facets
77
-
78
- # Convert the JSON hash back to a string
79
- JSON.generate(json_hash)
80
-
81
- # "Doesn't work yet"
47
+ def delete_record(collection, rkey)
48
+ data = { collection: collection, repo: session.did, rkey: rkey }
49
+ HTTParty.post(
50
+ delete_record_uri(session),
51
+ body: data.to_json,
52
+ headers: default_authenticated_headers(session),
53
+ )
82
54
  end
83
55
 
84
56
  def create_post(text)
@@ -89,34 +61,36 @@ module Bskyrb
89
61
  "record" => {
90
62
  "$type" => "app.bsky.feed.post",
91
63
  "createdAt" => DateTime.now.iso8601(3),
92
- "text" => text
93
- }
64
+ "text" => text,
65
+ },
94
66
  })
95
67
  create_record(input)
96
68
  end
97
69
 
98
70
  def create_reply(replylink, text)
99
71
  reply_to = get_post_by_url(replylink)
100
- reply_json = {
101
- root: {
102
- uri: reply_to.uri,
103
- cid: reply_to.cid,
104
- },
105
- parent: {
106
- uri: reply_to.uri,
107
- cid: reply_to.cid,
72
+ input = Bskyrb::ComAtprotoRepoCreaterecord::CreateRecord::Input.from_hash({
73
+ "collection" => "app.bsky.feed.post",
74
+ "$type" => "app.bsky.feed.post",
75
+ "repo" => session.did,
76
+
77
+ "record" => {
78
+ "reply" => {
79
+ "parent" => {
80
+ "uri" => reply_to.uri,
81
+ "cid" => reply_to.cid,
82
+ },
83
+ "root" => {
84
+ "uri" => reply_to.uri,
85
+ "cid" => reply_to.cid,
86
+ },
87
+ },
88
+ "$type" => "app.bsky.feed.post",
89
+ "createdAt" => DateTime.now.iso8601(3),
90
+ "text" => text,
108
91
  },
109
- collection: "app.bsky.feed.post",
110
- repo: session.did,
111
- record: {
112
- "$type": "app.bsky.feed.post",
113
- createdAt: DateTime.now.iso8601(3),
114
- text: text
115
- }
116
- },
117
- reply_hash = JSON.parse(reply_json.to_json)
118
- reply = Bskyrb::ComAtprotoRepoCreaterecord::CreateRecord::Input.from_hash(reply_hash)
119
- create_record(reply)
92
+ })
93
+ create_record(input)
120
94
  end
121
95
 
122
96
  def profile_action(username, type)
@@ -126,27 +100,26 @@ module Bskyrb
126
100
  "record" => {
127
101
  "subject" => resolve_handle(session.pds, username)["did"],
128
102
  "createdAt" => DateTime.now.iso8601(3),
129
- "$type" => type
130
- }
103
+ "$type" => type,
104
+ },
131
105
  })
132
106
  create_record(input)
133
107
  end
134
108
 
135
-
136
109
  def post_action(post, action_type)
137
110
  data = {
138
111
  collection: action_type,
139
112
  repo: session.did,
140
113
  record: {
141
- subject: {
142
- uri: post.uri,
143
- cid: post.cid,
144
- },
145
- createdAt: DateTime.now.iso8601(3),
146
- "$type": action_type
147
- }
148
- }
149
- create_record(data)
114
+ subject: {
115
+ uri: post.uri,
116
+ cid: post.cid,
117
+ },
118
+ createdAt: DateTime.now.iso8601(3),
119
+ "$type": action_type,
120
+ },
121
+ }
122
+ create_record(data)
150
123
  end
151
124
 
152
125
  def like(post_url)
@@ -158,29 +131,15 @@ module Bskyrb
158
131
  post = get_post_by_url(post_url)
159
132
  post_action(post, "app.bsky.feed.repost")
160
133
  end
161
-
134
+
162
135
  def follow(username)
163
136
  profile_action(username, "app.bsky.graph.follow")
164
137
  end
165
138
 
166
- # def unfollow(username)
167
- # profile_action(username, "app.bsky.graph.unfollow(?)")
168
- # end
169
- #NONE OF THESE WORK
170
- # def mute(username)
171
- # profile_action(username, "app.bsky.graph.mute")
172
- # end
173
-
174
- # def unmute(username)
175
- # profile_action(username, "app.bsky.graph.unmute")
176
- # end
177
-
178
-
179
139
  def block(username)
180
140
  profile_action(username, "app.bsky.graph.block")
181
141
  end
182
142
 
183
-
184
143
  def get_latest_post(username)
185
144
  feed = get_latest_n_posts(username, 1)
186
145
  feed.feed.first
@@ -193,7 +152,7 @@ module Bskyrb
193
152
  end
194
153
  hydrate_feed HTTParty.get(
195
154
  get_author_feed_uri(session.pds, query),
196
- headers: default_authenticated_headers(session)
155
+ headers: default_authenticated_headers(session),
197
156
  ), Bskyrb::AppBskyFeedGetauthorfeed::GetAuthorFeed::Output
198
157
  end
199
158
 
@@ -203,7 +162,7 @@ module Bskyrb
203
162
  end
204
163
  hydrate_feed HTTParty.get(
205
164
  get_timeline_uri(session.pds, query),
206
- headers: default_authenticated_headers(session)
165
+ headers: default_authenticated_headers(session),
207
166
  ), Bskyrb::AppBskyFeedGettimeline::GetTimeline::Output
208
167
  end
209
168
 
@@ -213,7 +172,7 @@ module Bskyrb
213
172
  end
214
173
  hydrate_feed HTTParty.get(
215
174
  get_popular_uri(session.pds, query),
216
- headers: default_authenticated_headers(session)
175
+ headers: default_authenticated_headers(session),
217
176
  ), Bskyrb::AppBskyUnspeccedGetpopular::GetPopular::Output
218
177
  end
219
178
 
@@ -1,28 +1,12 @@
1
+ # typed: false
1
2
  require "uri"
2
3
  require "httparty"
3
4
 
4
- # def bsky_register(pds, user, password, invcode, email) # lets you create new accounts on a pds directly from ruby
5
- # # doesn't go within session class because it wouldn't work unless you already have an account
6
- # data = {
7
- # "email": email,
8
- # "handle": user + ".bsky.social", # defaulting to bsky.social handles because you can't add dns records for a DID that doesn't exist yet
9
- # "inviteCode": invcode,
10
- # "password": password,
11
- # }
12
- # resp = HTTParty.post(
13
- # "#{pds}/xrpc/com.atproto.server.createAccount",
14
- # body: data.to_json,
15
- # headers: {'Content-Type' => 'application/json'}
16
- # )
17
- # resp
18
- # end
19
-
20
- module Bskyrb
5
+ module Bskyrb
21
6
  module RequestUtils
22
7
  def resolve_handle(pds, username)
23
- HTTParty.get(
24
- "#{pds}/xrpc/com.atproto.identity.resolveHandle?handle=#{username}"
25
- )
8
+ resolveHandle = XRPC::Endpoint.new(pds, "com.atproto.identity.resolveHandle", :handle)
9
+ resolveHandle.get(handle: username)
26
10
  end
27
11
 
28
12
  def query_obj_to_query_params(q)
@@ -34,13 +18,17 @@ module Bskyrb
34
18
  end
35
19
 
36
20
  def default_headers
37
- {"Content-Type" => "application/json"}
21
+ { "Content-Type" => "application/json" }
38
22
  end
39
23
 
40
24
  def create_record_uri(pds)
41
25
  "#{pds}/xrpc/com.atproto.repo.createRecord"
42
26
  end
43
27
 
28
+ def delete_record_uri(pds)
29
+ "#{pds}/xrpc/com.atproto.repo.deleteRecord"
30
+ end
31
+
44
32
  def upload_blob_uri(pds)
45
33
  "#{pds}/xrpc/com.atproto.repo.uploadBlob"
46
34
  end
@@ -63,18 +51,39 @@ module Bskyrb
63
51
 
64
52
  def default_authenticated_headers(session)
65
53
  default_headers.merge({
66
- Authorization: "Bearer #{session.access_token}"
54
+ Authorization: "Bearer #{session.access_token}",
67
55
  })
68
56
  end
69
57
 
70
58
  def at_post_link(pds, url)
71
- # e.g. "https://staging.bsky.app/profile/naia.bsky.social/post/3jszsrnruws27"
72
59
  url = url.to_s
73
- # regex by chatgpt:
74
- raise "The provided URL #{url} does not match the expected schema" unless /https:\/\/[a-zA-Z0-9.-]+\/profile\/[a-zA-Z0-9.-]+\/post\/[a-zA-Z0-9.-]+/.match?(url)
60
+
61
+ # Check if the URL is already in AT format
62
+ if url.start_with?("at://")
63
+ # Validate the username and post ID in the URL
64
+ parts = url.split("/")
65
+ if parts.length == 5 && parts[3] == "app.bsky.feed.post"
66
+ username = parts[2]
67
+ post_id = parts[4]
68
+ if post_id
69
+ return url
70
+ end
71
+ end
72
+
73
+ # If the URL is not valid, raise an error
74
+ raise "The provided URL #{url} is not a valid AT URL"
75
+ end
76
+
77
+ # Validate the format of the regular URL and extract the username and post ID
78
+ regex = /https:\/\/[a-zA-Z0-9.-]+\/profile\/[a-zA-Z0-9.-]+\/post\/[a-zA-Z0-9.-]+/
79
+ raise "The provided URL #{url} does not match the expected schema" unless regex.match?(url)
75
80
  username = url.split("/")[-3]
76
- did = resolve_handle(pds, username)["did"]
77
81
  post_id = url.split("/")[-1]
82
+
83
+ # Validate the username and post ID in the AT URL
84
+ did = resolve_handle(pds, username)["did"]
85
+
86
+ # Construct the AT URL
78
87
  "at://#{did}/app.bsky.feed.post/#{post_id}"
79
88
  end
80
89
  end
@@ -103,14 +112,15 @@ module Bskyrb
103
112
  uri = URI("#{pds}/xrpc/com.atproto.server.createSession")
104
113
  response = HTTParty.post(
105
114
  uri,
106
- body: {identifier: credentials.username, password: credentials.pw}.to_json,
107
- headers: default_headers
115
+ body: { identifier: credentials.username, password: credentials.pw }.to_json,
116
+ headers: default_headers,
108
117
  )
118
+
119
+ raise UnauthorizedError if response.code == 401
120
+
109
121
  @access_token = response["accessJwt"]
110
122
  @refresh_token = response["refreshJwt"]
111
123
  @did = response["did"]
112
124
  end
113
125
  end
114
-
115
-
116
126
  end
@@ -1,5 +1,6 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Bskyrb
4
- VERSION = "0.5"
5
+ VERSION = "0.5.2"
5
6
  end
@@ -0,0 +1,24 @@
1
+ # typed: false
2
+ require "uri"
3
+ require "httparty"
4
+ require "json"
5
+
6
+ module XRPC
7
+ class Endpoint
8
+ attr_reader :request_uri
9
+
10
+ def initialize(pds, endpoint_location, *params)
11
+ @pds = pds
12
+ @endpoint_location = endpoint_location
13
+ @params = params
14
+ end
15
+
16
+ def get(params)
17
+ query_params = URI.encode_www_form(params)
18
+ @request_uri = URI("#{@pds}/xrpc/#{@endpoint_location}?#{query_params}")
19
+
20
+ response = HTTParty.get(@request_uri)
21
+ JSON.parse(response.body)
22
+ end
23
+ end
24
+ end
data/lib/bskyrb.rb CHANGED
@@ -1,5 +1,8 @@
1
+ # typed: strict
1
2
  # frozen_string_literal: true
2
3
 
4
+ require "bskyrb/error"
3
5
  require "bskyrb/session"
4
6
  require "bskyrb/records"
5
7
  require "bskyrb/generated_classes"
8
+ require "bskyrb/xrpc"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bskyrb
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shreyan Jain
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-05-02 00:00:00.000000000 Z
12
+ date: 2023-05-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -25,20 +25,6 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '2.0'
28
- - !ruby/object:Gem::Dependency
29
- name: net-http
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
28
  - !ruby/object:Gem::Dependency
43
29
  name: date
44
30
  requirement: !ruby/object:Gem::Requirement
@@ -76,11 +62,13 @@ extra_rdoc_files: []
76
62
  files:
77
63
  - "./lib/bskyrb.rb"
78
64
  - "./lib/bskyrb/codegen.rb"
65
+ - "./lib/bskyrb/error.rb"
79
66
  - "./lib/bskyrb/firehose.rb"
80
67
  - "./lib/bskyrb/generated_classes.rb"
81
68
  - "./lib/bskyrb/records.rb"
82
69
  - "./lib/bskyrb/session.rb"
83
70
  - "./lib/bskyrb/version.rb"
71
+ - "./lib/bskyrb/xrpc.rb"
84
72
  homepage: https://github.com/ShreyanJain9/bskyrb
85
73
  licenses:
86
74
  - MIT