radiator 0.3.15 → 0.4.0pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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