radiator 0.3.15 → 0.4.0pre1

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.
@@ -26,8 +26,12 @@ module Radiator
26
26
  # steem.post!(options)
27
27
  #
28
28
  class Chain
29
+ include Mixins::ActsAsPoster
30
+ include Mixins::ActsAsVoter
31
+ include Mixins::ActsAsWallet
32
+
29
33
  VALID_OPTIONS = %w(
30
- chain account_name wif
34
+ chain account_name wif url failover_urls
31
35
  ).map(&:to_sym)
32
36
  VALID_OPTIONS.each { |option| attr_accessor option }
33
37
 
@@ -44,290 +48,246 @@ module Radiator
44
48
  @account_name ||= ENV['ACCOUNT_NAME']
45
49
  @wif ||= ENV['WIF']
46
50
 
47
- raise ChainError, "Required option: chain" if @chain.nil?
48
- raise ChainError, "Required option: account_name, wif" if @account_name.nil? || @wif.nil?
49
-
50
51
  reset
51
52
  end
52
53
 
53
- # Clears out queued operations.
54
- def reset
55
- @operations = []
56
- end
57
-
58
- # Broadcast queued operations.
54
+ # Find a specific block by block number.
59
55
  #
60
- # @param auto_reset [boolean] clears operations no matter what, even if there's an error.
61
- def broadcast!(auto_reset = false)
62
- begin
63
- transaction = Radiator::Transaction.new(build_options)
64
- transaction.operations = @operations
65
- response = transaction.process(true)
66
- rescue => e
67
- reset if auto_reset
68
- raise e
69
- end
70
-
71
- if !!response.result
72
- reset
73
- response
74
- else
75
- reset if auto_reset
76
- ErrorParser.new(response)
77
- end
78
- end
79
-
80
- # Create a vote operation.
56
+ # Example:
81
57
  #
82
- # Examples:
58
+ # steem = Steem.new
59
+ # block = steem.find_block(12345678)
60
+ # transactions = block.transactions
83
61
  #
84
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
85
- # steem.vote(10000, 'author', 'permlink')
86
- # steem.broadcast!
62
+ # @param block_number [Fixnum]
63
+ # @return [Hash]
64
+ def find_block(block_number)
65
+ api.get_blocks(block_number).first
66
+ end
67
+
68
+ # Find a specific account by name.
87
69
  #
88
- # ... or ...
70
+ # Example:
89
71
  #
90
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
91
- # steem.vote(10000, '@author/permlink')
92
- # steem.broadcast!
72
+ # steem = Steem.new
73
+ # ned = steem.find_account('ned')
74
+ # vesting_shares = ned.vesting_shares
93
75
  #
94
- # @param weight [Integer] value between -10000 and 10000.
95
- # @param args [author, permlink || slug] pass either `author` and `permlink` or string containing both like `@author/permlink`.
96
- def vote(weight, *args)
97
- author, permlink = if args.size == 1
98
- author, permlink = parse_slug(args[0])
99
- else
100
- author, permlink = args
76
+ # @param account_name [String] Name of the account to find.
77
+ # @return [Hash]
78
+ def find_account(account_name)
79
+ api.get_accounts([account_name]) do |accounts, err|
80
+ raise ChainError, ErrorParser.new(err) if !!err
81
+
82
+ accounts[0]
101
83
  end
102
-
103
- @operations << {
104
- type: :vote,
105
- voter: account_name,
106
- author: author,
107
- permlink: permlink,
108
- weight: weight
109
- }
110
-
111
- self
112
84
  end
113
85
 
114
- # Create a vote operation and broadcasts it right away.
86
+ # Find a specific comment by author and permlink or slug.
115
87
  #
116
- # Examples:
88
+ # Example:
117
89
  #
118
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
119
- # steem.vote!(10000, 'author', 'permlink')
90
+ # steem = Steem.new
91
+ # comment = steem.find_comment('inertia', 'kinda-spooky') # by account, permlink
92
+ # active_votes = comment.active_votes
120
93
  #
121
94
  # ... or ...
122
95
  #
123
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
124
- # steem.vote!(10000, '@author/permlink')
96
+ # comment = steem.find_comment('@inertia/kinda-spooky') # by slug
125
97
  #
126
- # @see vote
127
- def vote!(weight, *args); vote(weight, *args).broadcast!(true); end
128
-
129
- # Creates a post operation.
130
- #
131
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
132
- # options = {
133
- # title: 'This is my fancy post title.',
134
- # body: 'This is my fancy post body.',
135
- # tags: %w(thess are my fancy tags)
136
- # }
137
- # steem.post(options)
138
- # steem.broadcast!
139
- #
140
- # @param options [Hash] options
141
- # @option options [String] :title Title of the post.
142
- # @option options [String] :body Body of the post.
143
- # @option options [Array<String>] :tags Tags of the post.
144
- # @option options [String] :permlink (automatic) Permlink of the post, defaults to formatted title.
145
- # @option options [String] :parent_permlink (automatic) Parent permlink of the post, defaults to first tag.
146
- # @option options [String] :parent_author (optional) Parent author of the post (only used if reply).
147
- # @option options [String] :max_accepted_payout (1000000.000 SBD) Maximum accepted payout, set to '0.000 SBD' to deline payout
148
- # @option options [Integer] :percent_steem_dollars (5000) Percent STEEM Dollars is used to set 50/50 or 100% STEEM Power
149
- # @option options [Integer] :allow_votes (true) Allow votes for this post.
150
- # @option options [Integer] :allow_curation_rewards (true) Allow curation rewards for this post.
151
- def post(options = {})
152
- tags = [options[:tags] || []].flatten
153
- title = options[:title].to_s
154
- permlink = options[:permlink] || title.downcase.gsub(/[^a-z0-9\-]+/, '-')
155
- parent_permlink = options[:parent_permlink] || tags[0]
156
-
157
- raise ChainError, 'At least one tag is required or set the parent_permlink directy.' if parent_permlink.nil?
158
-
159
- body = options[:body]
160
- parent_author = options[:parent_author] || ''
161
- max_accepted_payout = options[:max_accepted_payout] || default_max_acepted_payout
162
- percent_steem_dollars = options[:percent_steem_dollars]
163
- allow_votes = options[:allow_votes] || true
164
- allow_curation_rewards = options[:allow_curation_rewards] || true
165
- self_vote = options[:self_vote]
166
-
167
- tags.insert(0, parent_permlink)
168
- tags = tags.compact.uniq
169
-
170
- metadata = {
171
- app: Radiator::AGENT_ID
172
- }
173
- metadata[:tags] = tags if tags.any?
98
+ # @param args [String || Array<String>] Slug or author, permlink of comment.
99
+ # @return [Hash]
100
+ def find_comment(*args)
101
+ author, permlink = normalize_author_permlink(args)
174
102
 
175
- @operations << {
176
- type: :comment,
177
- parent_permlink: parent_permlink,
178
- author: account_name,
179
- permlink: permlink,
180
- title: title,
181
- body: body,
182
- json_metadata: metadata.to_json,
183
- parent_author: parent_author
184
- }
103
+ api.get_content(author, permlink) do |comment, err|
104
+ raise ChainError, ErrorParser.new(err) if !!err
105
+
106
+ comment unless comment.id == 0
107
+ end
108
+ end
109
+
110
+ # Current dynamic global properties, cached for 3 seconds. This is useful
111
+ # for reading properties without worrying about actually fetching it over
112
+ # rpc more than needed.
113
+ def properties
114
+ @properties ||= nil
185
115
 
186
- if (!!max_accepted_payout &&
187
- max_accepted_payout != default_max_acepted_payout
188
- ) || !!percent_steem_dollars || !allow_votes || !allow_curation_rewards
189
- @operations << {
190
- type: :comment_options,
191
- author: account_name,
192
- permlink: permlink,
193
- max_accepted_payout: max_accepted_payout,
194
- percent_steem_dollars: percent_steem_dollars,
195
- allow_votes: allow_votes,
196
- allow_curation_rewards: allow_curation_rewards,
197
- extensions: []
198
- }
116
+ if !!@properties && Time.now.utc - Time.parse(@properties.time + 'Z') > 3
117
+ @properties = nil
199
118
  end
200
119
 
201
- vote(self_vote, account_name, permlink) if !!self_vote
120
+ return @properties if !!@properties
202
121
 
203
- self
122
+ api.get_dynamic_global_properties do |properties|
123
+ @properties = properties
124
+ end
204
125
  end
205
126
 
206
- # Create a vote operation and broadcasts it right away.
207
- #
208
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
209
- # options = {
210
- # title: 'This is my fancy post title.',
211
- # body: 'This is my fancy post body.',
212
- # tags: %w(thess are my fancy tags)
213
- # }
214
- # steem.post!(options)
215
- #
216
- # @see post
217
- def post!(options = {}); post(options).broadcast!(true); end
127
+ def block_time
128
+ Time.parse(properties.time + 'Z')
129
+ end
218
130
 
219
- # Create a delete_comment operation.
220
- #
221
- # Examples:
131
+ # Returns the current base (e.g. STEEM) price in the vest asset (e.g.
132
+ # VESTS).
222
133
  #
223
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
224
- # steem.delete_comment('permlink')
225
- # steem.broadcast!
226
- #
227
- # @param permlink
228
- def delete_comment(permlink)
229
- @operations << {
230
- type: :delete_comment,
231
- author: account_name,
232
- permlink: permlink
233
- }
234
-
235
- self
134
+ def base_per_mvest
135
+ total_vesting_fund_steem = properties.total_vesting_fund_steem.to_f
136
+ total_vesting_shares_mvest = properties.total_vesting_shares.to_f / 1e6
137
+
138
+ total_vesting_fund_steem / total_vesting_shares_mvest
236
139
  end
237
140
 
238
- # Create a delete_comment operation and broadcasts it right away.
239
- #
240
- # Examples:
141
+ # Returns the current base (e.g. STEEM) price in the debt asset (e.g SBD).
241
142
  #
242
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
243
- # steem.delete_comment!('permlink')
244
- #
245
- # @see delete_comment
246
- def delete_comment!(permlink); delete_comment(permlink).broadcast!(true); end
143
+ def base_per_debt
144
+ api.get_feed_history do |feed_history|
145
+ current_median_history = feed_history.current_median_history
146
+ base = current_median_history.base
147
+ base = base.split(' ').first.to_f
148
+ quote = current_median_history.quote
149
+ quote = quote.split(' ').first.to_f
150
+
151
+ (base / quote) * base_per_mvest
152
+ end
153
+ end
247
154
 
248
- # Create a claim_reward_balance operation.
249
- #
250
- # Examples:
155
+ # List of accounts followed by account.
251
156
  #
252
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
253
- # steem.claim_reward_balance(reward_sbd: '100.000 SBD')
254
- # steem.broadcast!
255
- #
256
- # @param options [Hash] options
257
- # @option options [String] :reward_steem The amount of STEEM to claim, like: `100.000 STEEM`
258
- # @option options [String] :reward_sbd The amount of SBD to claim, like: `100.000 SBD`
259
- # @option options [String] :reward_vests The amount of VESTS to claim, like: `100.000000 VESTS`
260
- def claim_reward_balance(options)
261
- reward_steem = options[:reward_steem] || '0.000 STEEM'
262
- reward_sbd = options[:reward_sbd] || '0.000 SBD'
263
- reward_vests = options[:reward_vests] || '0.000000 VESTS'
157
+ # @param account_name String Name of the account.
158
+ # @return [Array<String>]
159
+ def followed_by(account_name)
160
+ return [] if account_name.nil?
264
161
 
265
- @operations << {
266
- type: :claim_reward_balance,
267
- account: account_name,
268
- reward_steem: reward_steem,
269
- reward_sbd: reward_sbd,
270
- reward_vests: reward_vests
271
- }
162
+ followers = []
163
+ count = -1
164
+
165
+ until count == followers.size
166
+ count = followers.size
167
+ follow_api.get_followers(account_name, followers.last, 'blog', 1000) do |follows, err|
168
+ raise ChainError, ErrorParser.new(err) if !!err
169
+
170
+ followers += follows.map(&:follower)
171
+ followers = followers.uniq
172
+ end
173
+ end
272
174
 
273
- self
175
+ followers
274
176
  end
275
177
 
276
- # Create a claim_reward_balance operation and broadcasts it right away.
178
+ # List of accounts following account.
277
179
  #
278
- # Examples:
279
- #
280
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
281
- # steem.claim_reward_balance!(reward_sbd: '100.000 SBD')
282
- #
283
- # @see claim_reward_balance
284
- def claim_reward_balance!(permlink); claim_reward_balance(permlink).broadcast!(true); end
180
+ # @param account_name String Name of the account.
181
+ # @return [Array<String>]
182
+ def following(account_name)
183
+ return [] if account_name.nil?
184
+
185
+ following = []
186
+ count = -1
187
+
188
+ until count == following.size
189
+ count = following.size
190
+ follow_api.get_following(account_name, following.last, 'blog', 100) do |follows, err|
191
+ raise ChainError, ErrorParser.new(err) if !!err
192
+
193
+ following += follows.map(&:following)
194
+ following = following.uniq
195
+ end
196
+ end
197
+
198
+ following
199
+ end
285
200
 
286
- # Create a transfer operation.
287
- #
288
- # steem = Steem.new(account_name: 'your account name', wif: 'your active wif')
289
- # steem.transfer(amount: '1.000 SBD', to: 'account name', memo: 'this is a memo')
290
- # steem.broadcast!
291
- #
292
- # @param options [Hash] options
293
- # @option options [String] :amount The amount to transfer, like: `100.000 STEEM`
294
- # @option options [String] :to The account receiving the transfer.
295
- # @option options [String] :memo ('') The memo for the transfer.
296
- def transfer(options = {})
297
- @operations << options.merge(type: :transfer, from: account_name)
201
+ # Clears out queued properties.
202
+ def reset_properties
203
+ @properties = nil
204
+ end
205
+
206
+ # Clears out queued operations.
207
+ def reset_operations
208
+ @operations = []
209
+ end
210
+
211
+ # Clears out all properties and operations.
212
+ def reset
213
+ reset_properties
214
+ reset_operations
298
215
 
299
- self
216
+ @api = @block_api = @follow_api = nil
300
217
  end
301
218
 
302
- # Create a transfer operation and broadcasts it right away.
303
- #
304
- # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
305
- # steem.transfer!(amount: '1.000 SBD', to: 'account name', memo: 'this is a memo')
219
+ # Broadcast queued operations.
306
220
  #
307
- # @see transfer
308
- def transfer!(options = {}); transfer(options).broadcast!(true); end
221
+ # @param auto_reset [boolean] clears operations no matter what, even if there's an error.
222
+ def broadcast!(auto_reset = false)
223
+ raise ChainError, "Required option: chain" if @chain.nil?
224
+ raise ChainError, "Required option: account_name, wif" if @account_name.nil? || @wif.nil?
225
+
226
+ begin
227
+ transaction = Radiator::Transaction.new(build_options)
228
+ transaction.operations = @operations
229
+ response = transaction.process(true)
230
+ rescue => e
231
+ reset if auto_reset
232
+ raise e
233
+ end
234
+
235
+ if !!response.result
236
+ reset
237
+ response
238
+ else
239
+ reset if auto_reset
240
+ ErrorParser.new(response)
241
+ end
242
+ end
309
243
  private
310
244
  def build_options
311
245
  {
312
246
  chain: chain,
313
- wif: wif
247
+ wif: wif,
248
+ url: url,
249
+ failover_urls: failover_urls
314
250
  }
315
251
  end
316
252
 
253
+ def api
254
+ @api ||= Api.new(build_options)
255
+ end
256
+
257
+ def block_api
258
+ @block_api ||= BlockApi.new(build_options)
259
+ end
260
+
261
+ def follow_api
262
+ @follow_api ||= FollowApi.new(build_options)
263
+ end
264
+
317
265
  def parse_slug(slug)
318
266
  slug = slug.split('@').last
319
267
  author = slug.split('/')[0]
320
268
  [author, slug.split('/')[1..-1].join('/')]
321
269
  end
322
270
 
271
+ def normalize_author_permlink(args)
272
+ if args.size == 1
273
+ case args[0]
274
+ when String then parse_slug(args[0])
275
+ when Hash then [args[0]['author'], args[0]['permlink']]
276
+ end
277
+ else
278
+ args
279
+ end
280
+ end
281
+
323
282
  def default_max_acepted_payout
324
283
  "1000000.000 #{default_debt_asset}"
325
284
  end
326
285
 
327
286
  def default_debt_asset
328
287
  case chain
329
- when :steem then 'SBD'
330
- when :golos then 'GBG'
288
+ when :steem then ChainConfig::NETWORKS_STEEM_DEBT_ASSET
289
+ when :golos then ChainConfig::NETWORKS_GOLOS_DEBT_ASSET
290
+ when :test then ChainConfig::NETWORKS_TEST_DEBT_ASSET
331
291
  else; raise ChainError, "Unknown chain: #{chain}"
332
292
  end
333
293
  end
@@ -1,3 +1,5 @@
1
+ require 'awesome_print'
2
+
1
3
  module Radiator
2
4
  class ErrorParser
3
5
  include Utils
@@ -116,11 +118,7 @@ module Radiator
116
118
  @can_reprepare = false
117
119
  end
118
120
  rescue => e
119
- if defined? ap
120
- ap error_perser_exception: e, original_response: response
121
- else
122
- puts({error_perser_exception: e, original_response: response}.inspect)
123
- end
121
+ ap error_perser_exception: e, original_response: response
124
122
 
125
123
  @expiry = false
126
124
  @can_retry = false
@@ -5,7 +5,9 @@ module Radiator
5
5
  #
6
6
  # @return [Logger]
7
7
  def self.logger
8
- @logger ||= Logger.new(STDOUT)
8
+ @@logger ||= Logger.new(STDOUT).tap do |logger|
9
+ logger.progname = 'radiator'
10
+ end
9
11
  end
10
12
 
11
13
  # Sets the logger that Radiator uses for reporting errors.
@@ -13,6 +15,6 @@ module Radiator
13
15
  # @param logger [Logger] The logger to set as Radiator's logger.
14
16
  # @return [void]
15
17
  def self.logger=(logger)
16
- @logger = logger
18
+ @@logger = logger
17
19
  end
18
20
  end
@@ -0,0 +1,124 @@
1
+ module Radiator
2
+ module Mixins
3
+ module ActsAsPoster
4
+ # Creates a post operation.
5
+ #
6
+ # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
7
+ # options = {
8
+ # title: 'This is my fancy post title.',
9
+ # body: 'This is my fancy post body.',
10
+ # tags: %w(thess are my fancy tags)
11
+ # }
12
+ # steem.post(options)
13
+ # steem.broadcast!
14
+ #
15
+ # @param options [Hash] options
16
+ # @option options [String] :title Title of the post.
17
+ # @option options [String] :body Body of the post.
18
+ # @option options [Array<String>] :tags Tags of the post.
19
+ # @option options [String] :permlink (automatic) Permlink of the post, defaults to formatted title.
20
+ # @option options [String] :parent_permlink (automatic) Parent permlink of the post, defaults to first tag.
21
+ # @option options [String] :parent_author (optional) Parent author of the post (only used if reply).
22
+ # @option options [String] :max_accepted_payout (1000000.000 SBD) Maximum accepted payout, set to '0.000 SBD' to deline payout
23
+ # @option options [Integer] :percent_steem_dollars (5000) Percent STEEM Dollars is used to set 50/50 or 100% STEEM Power
24
+ # @option options [Integer] :allow_votes (true) Allow votes for this post.
25
+ # @option options [Integer] :allow_curation_rewards (true) Allow curation rewards for this post.
26
+ def post(options = {})
27
+ tags = [options[:tags] || []].flatten
28
+ title = options[:title].to_s
29
+ permlink = options[:permlink] || title.downcase.gsub(/[^a-z0-9\-]+/, '-')
30
+ parent_permlink = options[:parent_permlink] || tags[0]
31
+
32
+ raise ChainError, 'At least one tag is required or set the parent_permlink directy.' if parent_permlink.nil?
33
+
34
+ body = options[:body]
35
+ parent_author = options[:parent_author] || ''
36
+ max_accepted_payout = options[:max_accepted_payout] || default_max_acepted_payout
37
+ percent_steem_dollars = options[:percent_steem_dollars]
38
+ allow_votes = options[:allow_votes] || true
39
+ allow_curation_rewards = options[:allow_curation_rewards] || true
40
+ self_vote = options[:self_vote]
41
+
42
+ tags.insert(0, parent_permlink)
43
+ tags = tags.compact.uniq
44
+
45
+ metadata = {
46
+ app: Radiator::AGENT_ID
47
+ }
48
+ metadata[:tags] = tags if tags.any?
49
+
50
+ @operations << {
51
+ type: :comment,
52
+ parent_permlink: parent_permlink,
53
+ author: account_name,
54
+ permlink: permlink,
55
+ title: title,
56
+ body: body,
57
+ json_metadata: metadata.to_json,
58
+ parent_author: parent_author
59
+ }
60
+
61
+ if (!!max_accepted_payout &&
62
+ max_accepted_payout != default_max_acepted_payout
63
+ ) || !!percent_steem_dollars || !allow_votes || !allow_curation_rewards
64
+ @operations << {
65
+ type: :comment_options,
66
+ author: account_name,
67
+ permlink: permlink,
68
+ max_accepted_payout: max_accepted_payout,
69
+ percent_steem_dollars: percent_steem_dollars,
70
+ allow_votes: allow_votes,
71
+ allow_curation_rewards: allow_curation_rewards,
72
+ extensions: []
73
+ }
74
+ end
75
+
76
+ vote(self_vote, account_name, permlink) if !!self_vote
77
+
78
+ self
79
+ end
80
+
81
+ # Create a vote operation and broadcasts it right away.
82
+ #
83
+ # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
84
+ # options = {
85
+ # title: 'This is my fancy post title.',
86
+ # body: 'This is my fancy post body.',
87
+ # tags: %w(thess are my fancy tags)
88
+ # }
89
+ # steem.post!(options)
90
+ #
91
+ # @see post
92
+ def post!(options = {}); post(options).broadcast!(true); end
93
+
94
+ # Create a delete_comment operation.
95
+ #
96
+ # Examples:
97
+ #
98
+ # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
99
+ # steem.delete_comment('permlink')
100
+ # steem.broadcast!
101
+ #
102
+ # @param permlink
103
+ def delete_comment(permlink)
104
+ @operations << {
105
+ type: :delete_comment,
106
+ author: account_name,
107
+ permlink: permlink
108
+ }
109
+
110
+ self
111
+ end
112
+
113
+ # Create a delete_comment operation and broadcasts it right away.
114
+ #
115
+ # Examples:
116
+ #
117
+ # steem = Steem.new(account_name: 'your account name', wif: 'your wif')
118
+ # steem.delete_comment!('permlink')
119
+ #
120
+ # @see delete_comment
121
+ def delete_comment!(permlink); delete_comment(permlink).broadcast!(true); end
122
+ end
123
+ end
124
+ end