ordinals 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +3 -0
- data/README.md +79 -21
- data/Rakefile +2 -1
- data/lib/ordinals/api.rb +81 -4
- data/lib/ordinals/cache.rb +208 -0
- data/lib/ordinals/sandbox.rb +127 -0
- data/lib/ordinals/version.rb +20 -0
- data/lib/ordinals.rb +65 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4dbc3bf008b7d878df205f804f9a30887c0602afc3e16ccb64ed3749be911d82
|
4
|
+
data.tar.gz: a5562501103ac3be5d475216c7c1ae45c5f4c1e519105dcdd8adf1250fe5184a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0140e9330cff62dc983f91a5a851cbfc3d188d3711537304e538d3ea8664d7cfebc76d9f19596db60f51b9d23f117f18ec44e9fcebb5dfdf6e72131a8b87b8f6
|
7
|
+
data.tar.gz: 7605cf138cf69f8e7b1cf3f8e7e06c837623e1720c558b85f345bb02eb8da07b48c4882a8822ec6e7c9992756a8cb1d04089a27164e71374a26e1f3784e07943
|
data/CHANGELOG.md
CHANGED
data/Manifest.txt
CHANGED
@@ -4,7 +4,10 @@ README.md
|
|
4
4
|
Rakefile
|
5
5
|
lib/ordinals.rb
|
6
6
|
lib/ordinals/api.rb
|
7
|
+
lib/ordinals/cache.rb
|
7
8
|
lib/ordinals/recursive/composite.rb
|
8
9
|
lib/ordinals/recursive/generator.rb
|
9
10
|
lib/ordinals/recursive/image.rb
|
11
|
+
lib/ordinals/sandbox.rb
|
10
12
|
lib/ordinals/stats.rb
|
13
|
+
lib/ordinals/version.rb
|
data/README.md
CHANGED
@@ -13,12 +13,15 @@ ordinals gem - ordinals (inscription) api wrapper & helpers for Bitcoin, Litcoin
|
|
13
13
|
|
14
14
|
|
15
15
|
For now ordinal inscription api queries are supported on
|
16
|
-
- Bitcoin (via <https://ordinals.com>)
|
17
|
-
- Litecoin (via <https://ordinalslite.com>)
|
18
|
-
- Dogecoin (via <https://doginals.com>)
|
16
|
+
- Bitcoin (via <https://ordinals.com>) and
|
17
|
+
- Litecoin (via <https://ordinalslite.com>)
|
19
18
|
|
19
|
+
<!--
|
20
|
+
chekc if doginals still in operation???
|
21
|
+
- Dogecoin (via <https://doginals.com>)
|
22
|
+
-->
|
20
23
|
|
21
|
-
Let's start querying for bitcoin (btc) ordinal inscriptions:
|
24
|
+
Let's start querying for bitcoin (btc) ordinal inscriptions by id:
|
22
25
|
|
23
26
|
``` ruby
|
24
27
|
require 'ordinals'
|
@@ -44,20 +47,50 @@ pp data
|
|
44
47
|
# "address"=>"bc1pyncs638hxfhaqjvnedw2lyc2jta6zuvr9erj6m9cwlghp6qqwvhqeq574l",
|
45
48
|
# "output value"=>"9667",
|
46
49
|
# "sat"=>"1391087135005306",
|
47
|
-
# "
|
48
|
-
# "content"=>"
|
49
|
-
# "content length"=>"3072 bytes",
|
50
|
-
# "content type"=>"image/png",
|
50
|
+
# "content-length"=>"3072 bytes",
|
51
|
+
# "content-type"=>"image/png",
|
51
52
|
# "timestamp"=>"2023-02-18 03:02:18 UTC",
|
52
|
-
# "genesis
|
53
|
-
# "genesis
|
54
|
-
# "genesis
|
53
|
+
# "genesis-height"=>"777099",
|
54
|
+
# "genesis-fee"=>"1814",
|
55
|
+
# "genesis-transaction"=>"c41021cb11dce003e6a20a3420cf5954a1d104a1fe314393b915a62f020dcd0a",
|
55
56
|
# "location"=>"6a57dedb854c9bcac3fd2daab1172dd9a45f24c366dd8db33fe80557f11c6b39:22:0",
|
56
57
|
# "output"=>"6a57dedb854c9bcac3fd2daab1172dd9a45f24c366dd8db33fe80557f11c6b39:22",
|
57
58
|
# "offset"=>"0"}
|
58
59
|
```
|
59
60
|
|
60
61
|
|
62
|
+
Or by number (starting at 0):
|
63
|
+
|
64
|
+
``` ruby
|
65
|
+
data = Ordinals.inscription( 21_000_000 )
|
66
|
+
pp data
|
67
|
+
# GET https://ordinals.com/inscription/4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2i0...
|
68
|
+
#=> {"title"=>"Inscription 21000000",
|
69
|
+
# "id"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2i0",
|
70
|
+
# "address"=>"bc1qq7dx8c9842jm6kf4gn83mft07jqtvye0gm5sq9",
|
71
|
+
# "output-value"=>"330",
|
72
|
+
# "sat"=>"1894095647358032",
|
73
|
+
# "content-length"=>"57 bytes",
|
74
|
+
# "content-type"=>"text/plain",
|
75
|
+
# "timestamp"=>"2023-08-02 11:00:22 UTC",
|
76
|
+
# "genesis-height"=>"801343",
|
77
|
+
# "genesis-fee"=>"842",
|
78
|
+
# "genesis-transaction"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2",
|
79
|
+
# "location"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2:0:0",
|
80
|
+
# "output"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2:0",
|
81
|
+
# "offset"=>"0"}
|
82
|
+
|
83
|
+
content = Ordinals.content( 21_000_000 )
|
84
|
+
pp content
|
85
|
+
# GET https://ordinals.com/content/4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2i0...
|
86
|
+
#=> <Ordinals::Api::Content:0x0000028bd4c1ff18
|
87
|
+
# @data="{\"p\":\"brc-20\",\"op\":\"mint\",\"tick\":\"sats\",\"amt\":\"99999999\"}",
|
88
|
+
# @length=nil,
|
89
|
+
# @type="text/plain">
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
|
61
94
|
Let's try querying for litecoin (ltc) ordinal inscriptions:
|
62
95
|
|
63
96
|
``` ruby
|
@@ -83,18 +116,43 @@ pp data
|
|
83
116
|
#=> {"title"=>"Inscription 642",
|
84
117
|
# "id"=>"f2b6adf7b2d0f128fb14817ff37f5f36e9176b17571e20c49871875553f937b4i0",
|
85
118
|
# "address"=>"LSDz4fM4mLFyAZVoDuKyMHYq3af6cSZGSY",
|
86
|
-
# "output
|
87
|
-
# "
|
88
|
-
# "content"=>"
|
89
|
-
# "content length"=>"816 bytes",
|
90
|
-
# "content type"=>"image/png",
|
119
|
+
# "output-value"=>"9878",
|
120
|
+
# "content-length"=>"816 bytes",
|
121
|
+
# "content-type"=>"image/png",
|
91
122
|
# "timestamp"=>"2023-02-21 05:29:33 UTC",
|
92
|
-
# "genesis
|
93
|
-
# "genesis
|
94
|
-
# "genesis
|
123
|
+
# "genesis-height"=>"2426201",
|
124
|
+
# "genesis-fee"=>"361",
|
125
|
+
# "genesis-transaction"=>"f2b6adf7b2d0f128fb14817ff37f5f36e9176b17571e20c49871875553f937b4",
|
95
126
|
# "location"=>"6802c71b49f18aab91d0b363762b24afb1bf00c4820a5f782de6dd1b1cfbd68a:0:0",
|
96
127
|
# "output"=>"6802c71b49f18aab91d0b363762b24afb1bf00c4820a5f782de6dd1b1cfbd68a:0",
|
97
128
|
# "offset"=>"0"}
|
129
|
+
|
130
|
+
|
131
|
+
content = Ordinals.content( 0 )
|
132
|
+
pp content
|
133
|
+
# GET https://ordinalslite.com/content/71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0eei0...
|
134
|
+
#=> <Ordinals::Api::Content:0x00000242a9a874e0
|
135
|
+
# @data="%PDF-1.3\...",
|
136
|
+
# @length=nil,
|
137
|
+
# @type="application/pdf">
|
138
|
+
|
139
|
+
data = Ordinals.inscription( 0 )
|
140
|
+
pp data
|
141
|
+
# GET https://ordinalslite.com/inscription/71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0eei0...
|
142
|
+
#=> {"title"=>"Inscription 0",
|
143
|
+
# "id"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0eei0",
|
144
|
+
# "address"=>"ltc1qz7qy4m8nhfqggxrtj308njs52lu9f5l9v7d5wd",
|
145
|
+
# "output-value"=>"10000",
|
146
|
+
# "sat"=>"906691404536244",
|
147
|
+
# "content-length"=>"57237 bytes",
|
148
|
+
# "content-type"=>"application/pdf",
|
149
|
+
# "timestamp"=>"2023-02-19 20:26:43 UTC",
|
150
|
+
# "genesis-height"=>"2425370",
|
151
|
+
# "genesis-fee"=>"15968",
|
152
|
+
# "genesis-transaction"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0ee",
|
153
|
+
# "location"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0ee:0:0",
|
154
|
+
# "output"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0ee:0",
|
155
|
+
# "offset"=>"0"}
|
98
156
|
```
|
99
157
|
|
100
158
|
That's it.
|
@@ -267,7 +325,7 @@ composite = RecursiveImageComposite.new( 10, 10, width: 96,
|
|
267
325
|
data = read_json( "./ordinalpunks.json" )
|
268
326
|
puts " #{data['items'].size} inscribe(s)"
|
269
327
|
|
270
|
-
data['items'].
|
328
|
+
data['items'].each do |rec|
|
271
329
|
id = rec['inscription_id']
|
272
330
|
comment = rec['name']
|
273
331
|
|
@@ -353,7 +411,7 @@ Use it as you please with no restrictions whatsoever.
|
|
353
411
|
## Questions? Comments?
|
354
412
|
|
355
413
|
Join us in the [Ordgen / ORC-721 discord (chat server)](https://discord.gg/dDhvHKjm2t). Yes you can.
|
356
|
-
Your questions and
|
414
|
+
Your questions and commentary welcome.
|
357
415
|
|
358
416
|
|
359
417
|
Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'hoe'
|
2
|
+
require './lib/ordinals/version.rb'
|
2
3
|
|
3
4
|
|
4
5
|
###
|
@@ -10,7 +11,7 @@ require 'hoe'
|
|
10
11
|
|
11
12
|
Hoe.spec 'ordinals' do
|
12
13
|
|
13
|
-
self.version =
|
14
|
+
self.version = Ordinals::VERSION
|
14
15
|
|
15
16
|
self.summary = 'ordinals gem - ordinals (inscription) api wrapper & helpers for Bitcoin, Litecoin, Dogecoin & co.'
|
16
17
|
self.description = summary
|
data/lib/ordinals/api.rb
CHANGED
@@ -24,9 +24,13 @@ class Api ## change/rename Api to Client - why? why not?
|
|
24
24
|
end
|
25
25
|
|
26
26
|
|
27
|
+
def config() Ordinals.config; end ## convenience shortcut helper
|
28
|
+
|
27
29
|
def initialize( base, inscription: 'inscription' )
|
28
|
-
@base
|
29
|
-
@inscription
|
30
|
+
@base = base
|
31
|
+
@inscription = inscription
|
32
|
+
@requests = 0 ## count requests (for delay_in_s sleeping/throttling)
|
33
|
+
@pages = {} ## (0-)99, (100-)199, (200-)299 etc.
|
30
34
|
end
|
31
35
|
|
32
36
|
|
@@ -46,8 +50,9 @@ class Content
|
|
46
50
|
end ## (nested) class Content
|
47
51
|
|
48
52
|
|
53
|
+
def content( num_or_id )
|
54
|
+
id = num_or_id.is_a?( Integer ) ? _num_to_id( num_or_id ) : num_or_id
|
49
55
|
|
50
|
-
def content( id )
|
51
56
|
src = "#{@base}/content/#{id}"
|
52
57
|
res = get( src )
|
53
58
|
|
@@ -67,6 +72,56 @@ end ## (nested) class Content
|
|
67
72
|
end
|
68
73
|
|
69
74
|
|
75
|
+
INSCRIBE_ID_RX = %r{
|
76
|
+
inscription/(?<id>[a-fi0-9]+)
|
77
|
+
}ix
|
78
|
+
|
79
|
+
|
80
|
+
def inscription_ids( offset: ) ## note: page size is for now fixed 100
|
81
|
+
ids = []
|
82
|
+
|
83
|
+
src = "#{@base}/inscriptions/#{offset}"
|
84
|
+
res = get( src )
|
85
|
+
|
86
|
+
page = res.text ### assumes utf-8 for now
|
87
|
+
page.scan( INSCRIBE_ID_RX ) do |_|
|
88
|
+
m = Regexp.last_match
|
89
|
+
ids << m[:id]
|
90
|
+
end
|
91
|
+
puts " #{ids.size} inscribe id(s)"
|
92
|
+
ids = ids.reverse ## assume latest inscribe id is on top (reverse)
|
93
|
+
ids
|
94
|
+
end
|
95
|
+
|
96
|
+
def sub10k_ids
|
97
|
+
ids = []
|
98
|
+
limit = 100
|
99
|
+
|
100
|
+
100.times do |i| ## fetch first hundred (100*100=10000) inscribe ids
|
101
|
+
offset = 99 + limit*i
|
102
|
+
puts "==> #{i} - @ #{offset}..."
|
103
|
+
ids += inscription_ids( offset: offset )
|
104
|
+
end
|
105
|
+
puts " #{ids.size} inscribe id(s) - total"
|
106
|
+
ids
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def _num_to_id( num )
|
111
|
+
limit = 100
|
112
|
+
page, i = num.divmod( limit )
|
113
|
+
offset = 99+limit*page
|
114
|
+
## e.g. 100.divmod( 100 ) => [1,0]
|
115
|
+
## 100.divmod( 100 ) => [0,0]
|
116
|
+
## 99.divmod( 100 ) => [0,99]
|
117
|
+
## etc.
|
118
|
+
|
119
|
+
## auto-add to page cache
|
120
|
+
ids = @pages[ offset ] ||= inscription_ids( offset: offset )
|
121
|
+
ids[i]
|
122
|
+
end
|
123
|
+
|
124
|
+
|
70
125
|
=begin
|
71
126
|
<dl>
|
72
127
|
<dt>id</dt>
|
@@ -158,7 +213,9 @@ offset: 0
|
|
158
213
|
|
159
214
|
=end
|
160
215
|
|
161
|
-
def inscription(
|
216
|
+
def inscription( num_or_id )
|
217
|
+
id = num_or_id.is_a?( Integer ) ? _num_to_id( num_or_id ) : num_or_id
|
218
|
+
|
162
219
|
src = "#{@base}/#{@inscription}/#{id}"
|
163
220
|
res = get( src )
|
164
221
|
|
@@ -203,14 +260,34 @@ offset: 0
|
|
203
260
|
data[ k ] = v
|
204
261
|
end
|
205
262
|
data
|
263
|
+
|
264
|
+
|
265
|
+
## post process to convert to headers format
|
266
|
+
## e.g. replace space with -
|
267
|
+
## and remove/exclude content & preview
|
268
|
+
h = {}
|
269
|
+
data.each do |k,v|
|
270
|
+
next if ['preview', 'content'].include?( k )
|
271
|
+
h[ k.gsub( ' ', '-') ] = v
|
272
|
+
end
|
273
|
+
h
|
206
274
|
end
|
207
275
|
|
208
276
|
|
209
277
|
def get( src )
|
278
|
+
@requests += 1
|
279
|
+
|
280
|
+
if @requests > 1 && config.delay_in_s
|
281
|
+
puts "request no. #{@requests}@#{@base}; sleeping #{config.delay_in_s} sec(s)..."
|
282
|
+
sleep( config.delay_in_s )
|
283
|
+
end
|
284
|
+
|
210
285
|
res = Webclient.get( src )
|
286
|
+
|
211
287
|
if res.status.ok?
|
212
288
|
res
|
213
289
|
else
|
290
|
+
## todo/fix: raise exception here!!!!
|
214
291
|
puts "!! ERROR - HTTP #{res.status.code} #{res.status.message} - failed web request >#{src}<; sorry"
|
215
292
|
exit 1
|
216
293
|
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
|
2
|
+
module Ordinals
|
3
|
+
|
4
|
+
class Cache
|
5
|
+
|
6
|
+
def initialize( dir )
|
7
|
+
@dir = dir
|
8
|
+
@force = false
|
9
|
+
@delay_in_s = 0.5 ## wait 0.5s before next (possible) request
|
10
|
+
@requests = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
def json_to_txt( data )
|
16
|
+
buf = ''
|
17
|
+
data.each do |k,v|
|
18
|
+
next if ['preview', 'content'].include?( k )
|
19
|
+
buf << "#{k.gsub( ' ', '-')}: #{v}\n"
|
20
|
+
end
|
21
|
+
buf
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
def _find_blobs
|
27
|
+
## note: *.{txt,json,png} will also include *.meta.txt
|
28
|
+
### CANNOT use, thus, try *i?.*
|
29
|
+
Dir.glob( "#{@dir}/**/*i?.{txt,json,png}" )
|
30
|
+
end
|
31
|
+
|
32
|
+
def _find_meta
|
33
|
+
Dir.glob( "#{@dir}/**/*.meta.txt" )
|
34
|
+
end
|
35
|
+
|
36
|
+
def stats
|
37
|
+
paths = _find_meta
|
38
|
+
puts " #{paths.size} inscribe metadatafile(s) found"
|
39
|
+
|
40
|
+
paths = _find_blobs
|
41
|
+
puts " #{paths.size} inscribe blob(s) found"
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
def add( id )
|
50
|
+
## step 1) get metadata records
|
51
|
+
meta_path = "#{@dir}/#{id[0,2]}/#{id[2..-1]}.meta.txt"
|
52
|
+
|
53
|
+
meta = nil
|
54
|
+
if !@force && File.exist?( meta_path )
|
55
|
+
meta = read_headers( meta_path )
|
56
|
+
print "."
|
57
|
+
else
|
58
|
+
print " meta-#{id} "
|
59
|
+
sleep( @delay_in_s ) if @delay_in_s && @requests > 0
|
60
|
+
|
61
|
+
## fetch and cache in cache
|
62
|
+
meta = Ordinals.inscription( id )
|
63
|
+
pp meta
|
64
|
+
meta_txt = json_to_txt( meta )
|
65
|
+
|
66
|
+
write_text( meta_path, meta_txt )
|
67
|
+
@requests += 1
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
content_type = meta['content-type']
|
72
|
+
extname = if content_type.start_with?( 'text/plain' )
|
73
|
+
'.txt'
|
74
|
+
elsif content_type.start_with?( 'application/json' )
|
75
|
+
'.json'
|
76
|
+
elsif content_type.start_with?( 'image/png' )
|
77
|
+
'.png'
|
78
|
+
else
|
79
|
+
puts "!! ERROR - unexpected content type; got: #{content_type}"
|
80
|
+
## pp meta
|
81
|
+
exit 1
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
path = "#{@dir}/#{id[0,2]}/#{id[2..-1]}#{extname}"
|
86
|
+
if !@force && File.exist?( path )
|
87
|
+
## puts " in cache"
|
88
|
+
print "."
|
89
|
+
else
|
90
|
+
print " blob-#{id} "
|
91
|
+
sleep( @delay_in_s ) if @delay_in_s && @requests > 0
|
92
|
+
|
93
|
+
## note: save text as blob - byte-by-byte as is (might be corrupt text)
|
94
|
+
content = Ordinals.content( id )
|
95
|
+
## pp content
|
96
|
+
## puts "data:"
|
97
|
+
## puts content.data
|
98
|
+
|
99
|
+
write_blob( path, content.data )
|
100
|
+
end
|
101
|
+
end # method add
|
102
|
+
|
103
|
+
|
104
|
+
def _prepare_csv( path )
|
105
|
+
ids = []
|
106
|
+
recs = read_csv( path )
|
107
|
+
recs.each do |rec|
|
108
|
+
ids << rec['id']
|
109
|
+
end
|
110
|
+
ids
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_csv( path )
|
114
|
+
ids = _prepare_csv( path )
|
115
|
+
ids = ids.uniq ## make uniq by default - why? why not?
|
116
|
+
puts " #{ids.size} inscribe id(s)"
|
117
|
+
ids.each { |id| add(id) }
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
## get inscription links
|
123
|
+
## e.g.
|
124
|
+
## inscription/e8830c5fbdfc2f529350fd04a2ac20bb097dce98f159223837513ae3c8d05714i0
|
125
|
+
|
126
|
+
=begin
|
127
|
+
INSCRIBE_ID_RX = %r{
|
128
|
+
/b # boundry (non-alphanum)
|
129
|
+
(?<id>[a-f0-9]{64}i[0-9])
|
130
|
+
/b # boundry (non-alphanum)
|
131
|
+
}ix
|
132
|
+
=end
|
133
|
+
|
134
|
+
INSCRIBE_ID_RX = %r{
|
135
|
+
inscription/(?<id>[a-fi0-9]+)
|
136
|
+
}ix
|
137
|
+
|
138
|
+
|
139
|
+
##
|
140
|
+
## strict rx gets:
|
141
|
+
## 168 inscribe id(s)
|
142
|
+
## 118 inscribe id(s) - unique
|
143
|
+
##
|
144
|
+
## loose gets:
|
145
|
+
## 300 inscribe id(s)
|
146
|
+
## 250 inscribe id(s) - unique
|
147
|
+
##
|
148
|
+
## /b for boundray NOT working - why? why not?
|
149
|
+
|
150
|
+
|
151
|
+
def _prepare_html( path )
|
152
|
+
ids = []
|
153
|
+
txt = read_text( path )
|
154
|
+
txt.scan( INSCRIBE_ID_RX ) do |_|
|
155
|
+
m = Regexp.last_match
|
156
|
+
|
157
|
+
ids << m[:id]
|
158
|
+
end
|
159
|
+
ids
|
160
|
+
end
|
161
|
+
|
162
|
+
def add_html( path )
|
163
|
+
ids = _prepare_html( path )
|
164
|
+
ids = ids.uniq ## make uniq by default - why? why not?
|
165
|
+
puts " #{ids.size} inscribe id(s)"
|
166
|
+
ids.each { |id| add(id) }
|
167
|
+
end
|
168
|
+
|
169
|
+
def _prepare_dir( dir )
|
170
|
+
ids = []
|
171
|
+
|
172
|
+
## in html format
|
173
|
+
pages = Dir.glob( "#{dir}/**/*.html")
|
174
|
+
pages.each do |path|
|
175
|
+
ids += _prepare_html( path )
|
176
|
+
end
|
177
|
+
|
178
|
+
### in csv format
|
179
|
+
datasets = Dir.glob( "#{dir}/**/*.csv")
|
180
|
+
datasets.each do |path|
|
181
|
+
ids += _prepare_csv( path )
|
182
|
+
end
|
183
|
+
|
184
|
+
ids
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_dir( dir )
|
188
|
+
ids = _prepare_dir( dir )
|
189
|
+
puts " #{ids.size} inscribe id(s)"
|
190
|
+
## turn into symbol (to make uniq work ??)
|
191
|
+
ids = ids.uniq
|
192
|
+
pp ids
|
193
|
+
puts " #{ids.size} inscribe id(s) - unique"
|
194
|
+
ids.each { |id| add(id) }
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
|
199
|
+
def read( id )
|
200
|
+
_read_inscribe( "#{@dir}/#{id[0,2]}/#{id[2..-1]}.meta.txt" )
|
201
|
+
end
|
202
|
+
|
203
|
+
def _read_inscribe( path )
|
204
|
+
read_headers( path )
|
205
|
+
end
|
206
|
+
|
207
|
+
end # class Cache
|
208
|
+
end # module Ordinals
|
@@ -0,0 +1,127 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ordinals
|
4
|
+
class Sandbox
|
5
|
+
def initialize( dir='./content' )
|
6
|
+
@dir = dir
|
7
|
+
@force = false
|
8
|
+
@delay_in_s = 0.5 ## wait 0.5s before next (possible) request
|
9
|
+
@requests = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def exist?( id ) ### add alias for different name(s) - why? why not?
|
14
|
+
path = "#{@dir}/#{id}"
|
15
|
+
File.exist?( path )
|
16
|
+
end
|
17
|
+
|
18
|
+
def read( id )
|
19
|
+
path = "#{@dir}/#{id}"
|
20
|
+
read_blob( path )
|
21
|
+
end
|
22
|
+
|
23
|
+
def add( id )
|
24
|
+
path = "#{@dir}/#{id}"
|
25
|
+
if !@force && File.exist?( path )
|
26
|
+
## puts " in sandbox"
|
27
|
+
else
|
28
|
+
sleep( @delay_in_s ) if @delay_in_s && @requests > 0
|
29
|
+
|
30
|
+
## note: save text as blob - byte-by-byte as is (might be corrupt text)
|
31
|
+
content = Ordinals.content( id )
|
32
|
+
## pp content
|
33
|
+
#=> #<Ordinals::Api::Content:0x000001a1352df938
|
34
|
+
# @data="RIFF\xF8\v\x00\x00WEBPVP8 \xEC\v\x00\x00...",
|
35
|
+
# @length=3072,
|
36
|
+
# @type="image/png"
|
37
|
+
|
38
|
+
## puts "data:"
|
39
|
+
## puts content.data
|
40
|
+
write_blob( path, content.data )
|
41
|
+
@requests += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def add_csv( path )
|
47
|
+
recs = read_csv( path )
|
48
|
+
puts " #{recs.size} record(s)"
|
49
|
+
|
50
|
+
recs.each_with_index do |rec,i|
|
51
|
+
id = rec['id']
|
52
|
+
puts "==> #{i+1}/#{recs.size} - #{id}..."
|
53
|
+
add( id )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_collection( path )
|
58
|
+
data = read_json( path )
|
59
|
+
puts " #{data['items'].size} record(s)"
|
60
|
+
|
61
|
+
data['items'].each_with_index do |rec,i|
|
62
|
+
name = rec['name']
|
63
|
+
id = rec['inscription_id']
|
64
|
+
puts "==> #{i+1}/#{data['items'].size} - #{name} @ #{id}..."
|
65
|
+
add( id )
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
ID_RX = /[a-f0-9]{64}i[0-9]/x
|
72
|
+
|
73
|
+
def _find_ids( values )
|
74
|
+
values.select { |val| val.is_a?(String) && ID_RX.match(val) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_data( obj )
|
78
|
+
if obj.is_a?( Array )
|
79
|
+
recs = obj
|
80
|
+
puts " #{recs.size} record(s)"
|
81
|
+
recs.each_with_index do |rec,i|
|
82
|
+
if rec.is_a?( String )
|
83
|
+
id = rec
|
84
|
+
puts "==> #{i+1}/#{recs.size} #{id}..."
|
85
|
+
add( id )
|
86
|
+
else ## assume array of strings for now
|
87
|
+
values = rec
|
88
|
+
print "==> #{i+1}/#{recs.size} "
|
89
|
+
ids = _find_ids( values )
|
90
|
+
if ids.size == 1
|
91
|
+
id = ids[0]
|
92
|
+
puts " #{id}..."
|
93
|
+
add( id )
|
94
|
+
else
|
95
|
+
puts
|
96
|
+
if ids.empty?
|
97
|
+
puts "!! ERROR - no inscribe id found in data row:"
|
98
|
+
else
|
99
|
+
puts "!! ERROR - more than one (that is, #{ids.size}) inscribe id found in data row:"
|
100
|
+
end
|
101
|
+
pp values
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
else
|
107
|
+
## todo/fix: raise ArgumentError - why? why not?
|
108
|
+
puts "!! ERROR - sorry"
|
109
|
+
exit 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# todo/add
|
114
|
+
# def add_html( path )
|
115
|
+
# end
|
116
|
+
|
117
|
+
|
118
|
+
def add_dir( rootdir )
|
119
|
+
## glob for **.csv and **.html
|
120
|
+
datasets = Dir.glob( "#{rootdir}/**/*.csv" )
|
121
|
+
datasets.each do |path|
|
122
|
+
add_csv( path )
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end ## class Sandbox
|
126
|
+
end ## module Ordinals
|
127
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module Ordinals
|
3
|
+
# sync version w/ sport.db n friends - why? why not?
|
4
|
+
MAJOR = 1 ## todo: namespace inside version or something - why? why not??
|
5
|
+
MINOR = 2
|
6
|
+
PATCH = 0
|
7
|
+
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
8
|
+
|
9
|
+
def self.version
|
10
|
+
VERSION
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.banner
|
14
|
+
"ordinals/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in >#{root}<"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.root
|
18
|
+
File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
|
19
|
+
end
|
20
|
+
end
|
data/lib/ordinals.rb
CHANGED
@@ -5,6 +5,49 @@ require 'nokogiri'
|
|
5
5
|
|
6
6
|
|
7
7
|
|
8
|
+
### todo/fix:
|
9
|
+
### move upstream - read_headers
|
10
|
+
|
11
|
+
##
|
12
|
+
# note: key only supports a-z0-9 AND dash(-)
|
13
|
+
# no underscore(_) or dot(.)
|
14
|
+
#
|
15
|
+
# follow HTTP headers and domain names format
|
16
|
+
##
|
17
|
+
|
18
|
+
HEADER_RX = /\A(?<key>[a-z][a-z0-9-]*)
|
19
|
+
:
|
20
|
+
[ ]*
|
21
|
+
(?<value>.+?) ## non-greedy
|
22
|
+
\z
|
23
|
+
/x
|
24
|
+
|
25
|
+
|
26
|
+
def read_headers( path )
|
27
|
+
txt = read_text( path )
|
28
|
+
h = {}
|
29
|
+
txt.each_line do |line|
|
30
|
+
line = line.strip
|
31
|
+
## skip empty and comment lines
|
32
|
+
next if line.empty? || line.start_with?( '#' )
|
33
|
+
|
34
|
+
if m=HEADER_RX.match(line)
|
35
|
+
key = m[:key]
|
36
|
+
value = m[:value]
|
37
|
+
|
38
|
+
h[key] = value
|
39
|
+
else
|
40
|
+
puts "!! ERROR - parse error - no header pattern match for:"
|
41
|
+
puts line
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
h
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
8
51
|
|
9
52
|
module Ordinals
|
10
53
|
class Configuration
|
@@ -43,6 +86,15 @@ module Ordinals
|
|
43
86
|
self.chain = 'btc' unless defined?( @client )
|
44
87
|
@client
|
45
88
|
end
|
89
|
+
|
90
|
+
def delay_in_s
|
91
|
+
## note - default to 1 (sec) if not set
|
92
|
+
self.delay_in_s = 1 unless defined?( @delay_in_s )
|
93
|
+
@delay_in_s
|
94
|
+
end
|
95
|
+
def delay_in_s=(value) @delay_in_s = value; end
|
96
|
+
alias_method :sleep, :delay_in_s ## add sleep alias (or wait) - why? why not?
|
97
|
+
alias_method :sleep=, :delay_in_s=
|
46
98
|
end # class Configuration
|
47
99
|
|
48
100
|
|
@@ -76,15 +128,22 @@ module Ordinals
|
|
76
128
|
|
77
129
|
###################
|
78
130
|
### more convenience shortcuts
|
79
|
-
def self.inscription(
|
80
|
-
def self.content(
|
131
|
+
def self.inscription( num_or_id ) client.inscription( num_or_id ); end
|
132
|
+
def self.content( num_or_id ) client.content( num_or_id ); end
|
133
|
+
|
134
|
+
def self.inscription_ids( offset: ) client.inscription_ids( offset: offset ); end
|
135
|
+
def self.sub10k_ids() client.sub10k_ids; end
|
81
136
|
end # module Ordinals
|
82
137
|
|
83
138
|
|
84
139
|
|
85
140
|
|
86
141
|
## our own code
|
142
|
+
require_relative 'ordinals/version'
|
87
143
|
require_relative 'ordinals/api'
|
144
|
+
|
145
|
+
require_relative 'ordinals/cache'
|
146
|
+
require_relative 'ordinals/sandbox' ## (blob) "flat" content cache by inscribe id (./content)
|
88
147
|
require_relative 'ordinals/stats'
|
89
148
|
|
90
149
|
|
@@ -98,3 +157,7 @@ require_relative 'ordinals/recursive/generator'
|
|
98
157
|
RcsvImage = RecursiveImage
|
99
158
|
RcsvImageComposite = RecursiveImageComposite
|
100
159
|
|
160
|
+
|
161
|
+
|
162
|
+
# say hello
|
163
|
+
puts Ordinals.banner ## if defined?($RUBYCOCOS_DEBUG) && $RUBCOCOS_DEBUG
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ordinals
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cocos
|
@@ -88,10 +88,13 @@ files:
|
|
88
88
|
- Rakefile
|
89
89
|
- lib/ordinals.rb
|
90
90
|
- lib/ordinals/api.rb
|
91
|
+
- lib/ordinals/cache.rb
|
91
92
|
- lib/ordinals/recursive/composite.rb
|
92
93
|
- lib/ordinals/recursive/generator.rb
|
93
94
|
- lib/ordinals/recursive/image.rb
|
95
|
+
- lib/ordinals/sandbox.rb
|
94
96
|
- lib/ordinals/stats.rb
|
97
|
+
- lib/ordinals/version.rb
|
95
98
|
homepage: https://github.com/ordbase/ordbase
|
96
99
|
licenses:
|
97
100
|
- Public Domain
|