skypost 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 72251c65823ba0eb09ed0383726432a97ee1cb1359fef32441158e4c7c090c89
4
+ data.tar.gz: bd91c3aa9a407183191f6e6203a3279c7b76c4d4518fbd9355c6f08e3361d9d4
5
+ SHA512:
6
+ metadata.gz: aebd23ca3736be8ce1b4e80b084e28f5b3032557d747e0fd07186dd508775708f78339f0eff58789936559d3f0d78d96028e94509aa4c983e07bf5a1d498ea8e
7
+ data.tar.gz: fc2d2eb5d9bdf1a69b54c864f95aefd00b64f3d08cb56c8980e7848ad51447300624f3bb0fd8d3191559aabd39aaac6b845acdd6dfada4ddbff2e967e1381296
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Skypost
2
+
3
+ A Ruby gem for posting messages to Bluesky social network.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'skypost'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install skypost
20
+
21
+ ## Usage
22
+
23
+ ```ruby
24
+ require 'skypost'
25
+
26
+ # Initialize the client with your Bluesky credentials
27
+ # You can use either your custom domain or .bsky.social handle
28
+ client = Skypost::Client.new("username.com", "your-app-password")
29
+ # OR
30
+ client = Skypost::Client.new("username.bsky.social", "your-app-password")
31
+
32
+ # Post a message
33
+ client.post("Hello from Skypost!")
34
+
35
+ # Post a message with a clickable link
36
+ client.post('Check out this cool website: <a href="https://example.com">Example</a>!')
37
+ ```
38
+
39
+ ### App Password
40
+ To get your app password, go to your Bluesky account settings at [bsky.app/settings/app-passwords](https://bsky.app/settings/app-passwords) and create a new app password. Never use your main account password for API access.
41
+
42
+ ### Handle Format
43
+ You can use either:
44
+ 1. Your custom domain (if you have one), e.g., `username.com`
45
+ 2. Your Bluesky handle in the format `username.bsky.social`
46
+
47
+ To find your handle, look at your profile URL in Bluesky. For example:
48
+ - If your profile is at `https://bsky.app/profile/username.com`, use `username.com`
49
+ - If your profile is at `https://bsky.app/profile/username.bsky.social`, use `username.bsky.social`
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bundle install` to install dependencies.
54
+
55
+ ## Contributing
56
+
57
+ Bug reports and pull requests are welcome on GitHub.
58
+
59
+ ## License
60
+
61
+ The gem is available as open source under the terms of the MIT License.
@@ -0,0 +1,116 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Skypost
5
+ class Client
6
+ BASE_URL = "https://bsky.social"
7
+
8
+ class AuthenticationError < StandardError; end
9
+ class ValidationError < StandardError; end
10
+
11
+ def initialize(identifier = nil, password = nil)
12
+ @identifier = identifier
13
+ @password = password
14
+ @session = nil
15
+ validate_identifier if identifier
16
+ end
17
+
18
+ def authenticate
19
+ raise AuthenticationError, "Identifier and password are required" if @identifier.nil? || @password.nil?
20
+
21
+ response = connection.post("/xrpc/com.atproto.server.createSession") do |req|
22
+ req.headers["Content-Type"] = "application/json"
23
+ req.body = JSON.generate({
24
+ identifier: @identifier,
25
+ password: @password
26
+ })
27
+ end
28
+
29
+ @session = JSON.parse(response.body)
30
+ @session
31
+ rescue Faraday::ResourceNotFound => e
32
+ raise AuthenticationError, "Authentication failed: Invalid credentials or incorrect handle format. Your handle should be either your custom domain (e.g., 'username.com') or your Bluesky handle (e.g., 'username.bsky.social')"
33
+ rescue Faraday::Error => e
34
+ raise AuthenticationError, "Failed to authenticate: #{e.message}"
35
+ end
36
+
37
+ def post(text)
38
+ ensure_authenticated
39
+
40
+ current_time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
41
+ facets = extract_links(text)
42
+
43
+ request_body = {
44
+ repo: @session["did"],
45
+ collection: "app.bsky.feed.post",
46
+ record: {
47
+ text: text.gsub(/<a href="[^"]*">|<\/a>/, ''), # Remove HTML tags but keep link text
48
+ facets: facets,
49
+ createdAt: current_time,
50
+ "$type": "app.bsky.feed.post"
51
+ }
52
+ }
53
+
54
+ response = connection.post("/xrpc/com.atproto.repo.createRecord") do |req|
55
+ req.headers["Content-Type"] = "application/json"
56
+ req.headers["Authorization"] = "Bearer #{@session["accessJwt"]}"
57
+ req.body = JSON.generate(request_body)
58
+ end
59
+
60
+ JSON.parse(response.body)
61
+ rescue Faraday::ResourceNotFound => e
62
+ raise "Failed to post: The API endpoint returned 404. Please check if you're authenticated and using the correct API endpoint."
63
+ rescue Faraday::Error => e
64
+ raise "Failed to post: #{e.message}"
65
+ end
66
+
67
+ private
68
+
69
+ def validate_identifier
70
+ unless @identifier.include?(".")
71
+ raise ValidationError, "Invalid handle format. Handle must be either a custom domain (e.g., 'username.com') or a Bluesky handle (e.g., 'username.bsky.social')"
72
+ end
73
+ end
74
+
75
+ def ensure_authenticated
76
+ authenticate if @session.nil?
77
+ end
78
+
79
+ def connection
80
+ @connection ||= Faraday.new(url: BASE_URL) do |f|
81
+ f.request :json
82
+ f.response :raise_error
83
+ end
84
+ end
85
+
86
+ def extract_links(text)
87
+ facets = []
88
+ link_pattern = /<a href="([^"]*)">(.*?)<\/a>/
89
+
90
+ # First, find all matches to calculate correct byte positions
91
+ matches = text.to_enum(:scan, link_pattern).map { Regexp.last_match }
92
+ plain_text = text.gsub(/<a href="[^"]*">|<\/a>/, '') # Text with HTML removed
93
+
94
+ matches.each do |match|
95
+ url = match[1]
96
+ link_text = match[2]
97
+
98
+ # Find the link text in the plain text to get correct byte positions
99
+ if link_position = plain_text.index(link_text)
100
+ facets << {
101
+ index: {
102
+ byteStart: link_position,
103
+ byteEnd: link_position + link_text.bytesize
104
+ },
105
+ features: [{
106
+ "$type": "app.bsky.richtext.facet#link",
107
+ uri: url
108
+ }]
109
+ }
110
+ end
111
+ end
112
+
113
+ facets
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,3 @@
1
+ module Skypost
2
+ VERSION = "0.0.1"
3
+ end
data/lib/skypost.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "faraday"
2
+ require "json"
3
+ require_relative "skypost/client"
4
+ require_relative "skypost/version"
5
+
6
+ module Skypost
7
+ class Error < StandardError; end
8
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: skypost
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jeroen Roosenboom
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-11-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '13.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '13.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ description: A simple Ruby gem that allows posting messages to the Bluesky social
84
+ network
85
+ email:
86
+ - hi@jro7.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - README.md
92
+ - lib/skypost.rb
93
+ - lib/skypost/client.rb
94
+ - lib/skypost/version.rb
95
+ homepage: https://github.com/jro7/skypost
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 2.6.0
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.5.23
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: A Ruby gem for posting to Bluesky
118
+ test_files: []