bskyrb 0.5 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.1"
5
6
  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 "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.1
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,6 +62,7 @@ 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"