cosgrove 0.0.2 → 0.0.3rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -0
- data/Gemfile.lock +57 -40
- data/README.md +1 -1
- data/cosgrove.gemspec +3 -3
- data/lib/cosgrove.rb +6 -1
- data/lib/cosgrove/account.rb +11 -6
- data/lib/cosgrove/bot.rb +15 -26
- data/lib/cosgrove/comment_job.rb +11 -14
- data/lib/cosgrove/config.rb +1 -1
- data/lib/cosgrove/market.rb +76 -64
- data/lib/cosgrove/support.rb +70 -21
- data/lib/cosgrove/upvote_job.rb +67 -29
- data/lib/cosgrove/utils.rb +132 -24
- data/lib/cosgrove/version.rb +1 -1
- metadata +12 -32
data/lib/cosgrove/config.rb
CHANGED
data/lib/cosgrove/market.rb
CHANGED
@@ -4,18 +4,19 @@ module Cosgrove
|
|
4
4
|
include ActionView::Helpers::NumberHelper
|
5
5
|
|
6
6
|
def price_feed(chain = :steem)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
api(chain).get_feed_history do |feed_history|
|
8
|
+
base_per_mvest = api(chain).steem_per_mvest
|
9
|
+
|
10
|
+
current_median_history = feed_history.current_median_history
|
11
|
+
base = current_median_history.base
|
12
|
+
base = base.split(' ').first.to_f
|
13
|
+
quote = current_median_history.quote
|
14
|
+
quote = quote.split(' ').first.to_f
|
15
|
+
|
16
|
+
base_per_debt = (base / quote) * base_per_mvest
|
17
|
+
|
18
|
+
[base_per_mvest, base_per_debt]
|
19
|
+
end
|
19
20
|
end
|
20
21
|
|
21
22
|
# https://api.vaultoro.com/#api-Basic_API-getMarkets
|
@@ -113,13 +114,12 @@ module Cosgrove
|
|
113
114
|
if a =~ /.*\*$/
|
114
115
|
wildcards = true
|
115
116
|
lower_bound_name = a.split('*').first
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
account_names += result.map { |name| name if name =~ /#{lower_bound_name}.*/ }.compact
|
117
|
+
api(chain).lookup_accounts(a, 1000) do |names, error|
|
118
|
+
if names.any?
|
119
|
+
account_names -= [a]
|
120
|
+
account_names += [lower_bound_name]
|
121
|
+
account_names += names.map { |name| name if name =~ /#{lower_bound_name}.*/ }.compact
|
122
|
+
end
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
@@ -128,10 +128,21 @@ module Cosgrove
|
|
128
128
|
accounts = SteemData::Account.where(:name.in => account_names)
|
129
129
|
account = accounts.last
|
130
130
|
|
131
|
+
if accounts.size == 0
|
132
|
+
accounts = SteemApi::Account.where(name: account_names)
|
133
|
+
account = accounts.limit(1).last
|
134
|
+
end
|
135
|
+
|
131
136
|
if accounts.count == account_names.size
|
132
|
-
|
133
|
-
|
134
|
-
|
137
|
+
if accounts.kind_of? Mongoid::Criteria
|
138
|
+
vests = accounts.sum('vesting_shares.amount')
|
139
|
+
delegated_vests = accounts.sum('delegated_vesting_shares.amount')
|
140
|
+
received_vests = accounts.sum('received_vesting_shares.amount')
|
141
|
+
else
|
142
|
+
vests = accounts.pluck(:vesting_shares).map(&:to_f).sum
|
143
|
+
delegated_vests = accounts.pluck(:delegated_vesting_shares).map(&:to_f).sum
|
144
|
+
received_vests = accounts.pluck(:received_vesting_shares).map(&:to_f).sum
|
145
|
+
end
|
135
146
|
elsif !wildcards
|
136
147
|
valid_names = accounts.distinct(:name)
|
137
148
|
unknown_names = account_names - valid_names
|
@@ -141,11 +152,12 @@ module Cosgrove
|
|
141
152
|
if accounts.count == 1 && vests == 0.0
|
142
153
|
# Falling back to RPC because balance is out of date and we only want
|
143
154
|
# a single account.
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
155
|
+
api(:steem).get_accounts([account.name]) do |accounts|
|
156
|
+
account = accounts.first
|
157
|
+
vests = account.vesting_shares.split(' ').first.to_f
|
158
|
+
delegated_vests = account.delegated_vesting_shares.split(' ').first.to_f
|
159
|
+
received_vests = account.received_vesting_shares.split(' ').first.to_f
|
160
|
+
end
|
149
161
|
end
|
150
162
|
|
151
163
|
mvests = vests / 1000000
|
@@ -177,9 +189,13 @@ module Cosgrove
|
|
177
189
|
"**#{pluralize(accounts.count, 'account')}:** `#{balance.join(' ')}`"
|
178
190
|
end
|
179
191
|
when :golos
|
180
|
-
|
181
|
-
|
182
|
-
|
192
|
+
account = nil
|
193
|
+
gests = nil
|
194
|
+
|
195
|
+
api(:golos).get_accounts(account_names) do |accounts, error|
|
196
|
+
account = accounts.first
|
197
|
+
gests = account.vesting_shares.split(' ').first.to_f
|
198
|
+
end
|
183
199
|
|
184
200
|
mgests = gests / 1000000
|
185
201
|
golos = base_per_mvest * mgests
|
@@ -203,10 +219,10 @@ module Cosgrove
|
|
203
219
|
|
204
220
|
case chain
|
205
221
|
when :steem
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
222
|
+
total = api(chain).get_reward_fund('post') do |reward_fund|
|
223
|
+
reward_fund.reward_balance.split(' ').first.to_f
|
224
|
+
end
|
225
|
+
|
210
226
|
total_usd = (total / base_per_mvest) * base_per_debt
|
211
227
|
btx_total_usd = total * btx_usdt_steem
|
212
228
|
|
@@ -216,10 +232,10 @@ module Cosgrove
|
|
216
232
|
|
217
233
|
"Total Reward Fund: `#{total} STEEM (Worth: #{total_usd} internally; #{btx_total_usd} on Bittrex)`"
|
218
234
|
when :golos
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
235
|
+
total = api(chain).get_dynamic_global_properties do |properties|
|
236
|
+
properties.total_reward_fund_steem.split(' ').first.to_f
|
237
|
+
end
|
238
|
+
|
223
239
|
total_gbg = (total / base_per_mvest) * base_per_debt
|
224
240
|
btx_total_usd = total * btx_usdt_golos
|
225
241
|
|
@@ -258,10 +274,9 @@ module Cosgrove
|
|
258
274
|
sum_promoted = promoted.sum('amount.amount')
|
259
275
|
|
260
276
|
base_per_mvest, base_per_debt = price_feed(chain)
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
total = total.split(' ').first.to_f
|
277
|
+
total = api(chain).get_reward_fund('post') do |reward_fund|
|
278
|
+
reward_fund.reward_balance.split(' ').first.to_f
|
279
|
+
end
|
265
280
|
|
266
281
|
total_usd = (total / base_per_mvest) * base_per_debt
|
267
282
|
ratio = (sum_promoted / total_usd) * 100
|
@@ -274,23 +289,22 @@ module Cosgrove
|
|
274
289
|
|
275
290
|
def supply(chain)
|
276
291
|
base_per_mvest, base_per_debt = price_feed(chain)
|
277
|
-
|
278
|
-
|
279
|
-
|
292
|
+
properties = api(chain).get_dynamic_global_properties do |_properties, error|
|
293
|
+
_properties
|
294
|
+
end
|
295
|
+
|
280
296
|
current_supply = properties.current_supply.split(' ').first.to_f
|
281
297
|
current_debt_supply = properties.current_sbd_supply.split(' ').first.to_f
|
282
298
|
total_vesting_fund_steem = properties.total_vesting_fund_steem.split(' ').first.to_f
|
283
|
-
|
284
299
|
total_base = (current_supply / base_per_mvest) * base_per_debt
|
285
300
|
ratio = (current_debt_supply / total_base) * 100
|
286
301
|
|
287
302
|
supply = []
|
288
303
|
case chain
|
289
304
|
when :steem
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
reward_balance = reward_balance.split(' ').first.to_f
|
305
|
+
reward_balance = api(chain).get_reward_fund('post') do |reward_fund|
|
306
|
+
reward_fund.reward_balance.split(' ').first.to_f
|
307
|
+
end
|
294
308
|
|
295
309
|
liquid_supply = current_supply - reward_balance - total_vesting_fund_steem
|
296
310
|
ratio_liquid = (liquid_supply / current_supply) * 100
|
@@ -306,7 +320,6 @@ module Cosgrove
|
|
306
320
|
supply << ["#{current_debt_supply} SBD (#{ratio} of supply)"]
|
307
321
|
supply << ["#{liquid_supply} Liquid STEEM (#{ratio_liquid} of supply)"]
|
308
322
|
when :golos
|
309
|
-
properties = response.result
|
310
323
|
reward_balance = properties.total_reward_fund_steem
|
311
324
|
reward_balance = reward_balance.split(' ').first.to_f
|
312
325
|
|
@@ -324,7 +337,6 @@ module Cosgrove
|
|
324
337
|
supply << ["#{current_debt_supply} GBG (#{ratio} of supply)"]
|
325
338
|
supply << ["#{liquid_supply} Liquid GOLOS (#{ratio_liquid} of supply)"]
|
326
339
|
when :test
|
327
|
-
properties = response.result
|
328
340
|
reward_balance = properties.total_reward_fund_steem
|
329
341
|
reward_balance = reward_balance.split(' ').first.to_f
|
330
342
|
|
@@ -358,9 +370,9 @@ module Cosgrove
|
|
358
370
|
|
359
371
|
def debt_exchange_rate(chain = :steem, limit = 19)
|
360
372
|
chain = chain.to_sym
|
361
|
-
|
362
|
-
|
363
|
-
|
373
|
+
rates = api(chain).get_witnesses_by_vote('', limit) do |witnesses|
|
374
|
+
witnesses.map(&:sbd_exchange_rate)
|
375
|
+
end
|
364
376
|
|
365
377
|
symbol = case chain
|
366
378
|
when :steem then 'SBD'
|
@@ -382,9 +394,9 @@ module Cosgrove
|
|
382
394
|
|
383
395
|
def apr(chain = :steem, limit = 19)
|
384
396
|
chain = chain.to_sym
|
385
|
-
|
386
|
-
|
387
|
-
|
397
|
+
rates = api(chain).get_witnesses_by_vote('', limit) do |witnesses|
|
398
|
+
witnesses.map(&:props).map { |p| p['sbd_interest_rate'] }
|
399
|
+
end
|
388
400
|
|
389
401
|
rate = rates.sum / rates.size
|
390
402
|
rate = rate / 100.0
|
@@ -393,9 +405,9 @@ module Cosgrove
|
|
393
405
|
|
394
406
|
def effective_apr(chain = :steem)
|
395
407
|
chain = chain.to_sym
|
396
|
-
|
397
|
-
|
398
|
-
|
408
|
+
rate = api(chain).get_dynamic_global_properties do |properties|
|
409
|
+
properties.sbd_interest_rate
|
410
|
+
end
|
399
411
|
|
400
412
|
rate = rate / 100.0
|
401
413
|
number_to_percentage(rate, precision: 3)
|
@@ -403,9 +415,9 @@ module Cosgrove
|
|
403
415
|
|
404
416
|
def effective_price(chain = :steem)
|
405
417
|
chain = chain.to_sym
|
406
|
-
|
407
|
-
|
408
|
-
|
418
|
+
current_median_history = api(chain).get_feed_history do |feed_history|
|
419
|
+
feed_history.current_median_history
|
420
|
+
end
|
409
421
|
|
410
422
|
b = current_median_history.base.split(' ').first.to_f
|
411
423
|
q = current_median_history.quote.split(' ').first.to_f
|
data/lib/cosgrove/support.rb
CHANGED
@@ -29,12 +29,16 @@ module Cosgrove
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def mongo_behind_warning(event)
|
32
|
+
elapse = -1
|
33
|
+
|
32
34
|
begin
|
33
35
|
message = []
|
34
36
|
|
35
37
|
if (blocks = head_block_number(:steem) - steem_data_head_block_number) > 1200
|
36
38
|
elapse = blocks * 3
|
37
39
|
message << "Mongo is behind by #{time_ago_in_words(elapse.seconds.ago)}."
|
40
|
+
else
|
41
|
+
0
|
38
42
|
end
|
39
43
|
|
40
44
|
if message.size > 0
|
@@ -45,6 +49,8 @@ module Cosgrove
|
|
45
49
|
sleep 15
|
46
50
|
event.respond Cosgrove::SnarkCommands::WITTY.sample
|
47
51
|
end
|
52
|
+
|
53
|
+
elapse
|
48
54
|
end
|
49
55
|
|
50
56
|
def cannot_find_input(event, message_prefix = "Unable to find that.")
|
@@ -62,16 +68,31 @@ module Cosgrove
|
|
62
68
|
|
63
69
|
def append_link_details(event, slug)
|
64
70
|
author_name, permlink = parse_slug slug
|
71
|
+
created = nil
|
72
|
+
cashout_time = nil
|
73
|
+
|
74
|
+
if slug =~ /steemit.com/
|
75
|
+
chain = :steem
|
76
|
+
elsif slug =~ /golos.io/
|
77
|
+
chain = :golos
|
78
|
+
elsif slug =~ /golos.blog/
|
79
|
+
chain = :golos
|
80
|
+
else
|
81
|
+
return # silntlly ignore this slug
|
82
|
+
end
|
65
83
|
|
66
|
-
post =
|
84
|
+
post = case chain
|
85
|
+
when :steem then SteemData::Post.where(author: author_name, permlink: permlink).last
|
86
|
+
when :golos then GolosCloud::Comment.where(author: author_name, permlink: permlink).last
|
87
|
+
end
|
67
88
|
|
68
89
|
if post.nil?
|
69
90
|
# Fall back to RPC
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
91
|
+
api(chain).get_content(author_name, permlink) do |content, errors|
|
92
|
+
unless content.author.empty?
|
93
|
+
created = Time.parse(content.created + 'Z')
|
94
|
+
cashout_time = Time.parse(content.cashout_time + 'Z')
|
95
|
+
end
|
75
96
|
end
|
76
97
|
end
|
77
98
|
|
@@ -98,7 +119,10 @@ module Cosgrove
|
|
98
119
|
|
99
120
|
# Only append this detail of the post less than an hour old.
|
100
121
|
if created > 1.hour.ago
|
101
|
-
votes =
|
122
|
+
votes = case chain
|
123
|
+
when :steem then SteemData::AccountOperation.type('vote').starting(post.created)
|
124
|
+
when :golos then GolosCloud::Vote.where('timestamp > ?', post.created)
|
125
|
+
end
|
102
126
|
total_votes = votes.count
|
103
127
|
total_voters = votes.distinct(:voter).size
|
104
128
|
|
@@ -115,7 +139,7 @@ module Cosgrove
|
|
115
139
|
|
116
140
|
begin
|
117
141
|
event.respond details.join('; ')
|
118
|
-
rescue Discordrb::Errors::NoPermission =>
|
142
|
+
rescue Discordrb::Errors::NoPermission => _
|
119
143
|
puts "Unable to append link details on #{event.channel.server.name} in #{event.channel.name}"
|
120
144
|
end
|
121
145
|
|
@@ -124,19 +148,41 @@ module Cosgrove
|
|
124
148
|
|
125
149
|
def find_account(key, event = nil, chain = :steem)
|
126
150
|
key = key.to_s.downcase
|
151
|
+
chain = chain.to_sym
|
127
152
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
# Fall back to RPC
|
134
|
-
if !!key
|
135
|
-
response = api(chain).get_accounts([key])
|
136
|
-
return account = response.result.first
|
153
|
+
raise "Required argument: chain" if chain.nil?
|
154
|
+
|
155
|
+
if chain == :steem
|
156
|
+
account = if (accounts = SteemData::Account.where(name: key)).any?
|
157
|
+
accounts.first
|
137
158
|
end
|
159
|
+
end
|
160
|
+
|
161
|
+
if account.nil?
|
162
|
+
account = if !!(cb_account = Cosgrove::Account.find_by_discord_id(key, chain))
|
163
|
+
cb_account.chain_account
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
if account.nil?
|
168
|
+
account = if !!key
|
169
|
+
if chain == :steem && (accounts = SteemApi::Account.where(name: key)).any?
|
170
|
+
accounts.first
|
171
|
+
elsif chain == :golos && (accounts = GolosCloud::Account.where(name: key)).any?
|
172
|
+
accounts.first
|
173
|
+
else
|
174
|
+
# Fall back to RPC
|
175
|
+
api(chain).get_accounts([key]) do |_accounts, errors|
|
176
|
+
_accounts.first
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
138
181
|
|
139
|
-
|
182
|
+
if account.nil?
|
183
|
+
unknown_account(key, event)
|
184
|
+
else
|
185
|
+
account
|
140
186
|
end
|
141
187
|
end
|
142
188
|
|
@@ -204,9 +250,12 @@ module Cosgrove
|
|
204
250
|
count = -1
|
205
251
|
until count == ignoring.size
|
206
252
|
count = ignoring.size
|
207
|
-
|
208
|
-
|
209
|
-
|
253
|
+
follow_api(chain).get_following(a, ignoring.last, 'ignore', 100) do |ignores, errors|
|
254
|
+
next unless defined? ignores.following
|
255
|
+
|
256
|
+
ignoring += ignores.map(&:following)
|
257
|
+
ignoring = ignoring.uniq
|
258
|
+
end
|
210
259
|
end
|
211
260
|
muted += ignoring
|
212
261
|
end
|
data/lib/cosgrove/upvote_job.rb
CHANGED
@@ -22,7 +22,13 @@ module Cosgrove
|
|
22
22
|
muters << steem_account
|
23
23
|
muted = muted by: muters, chain: :steem
|
24
24
|
|
25
|
-
|
25
|
+
post = find_comment(author: author_name, permlink: permlink)
|
26
|
+
|
27
|
+
if post.nil?
|
28
|
+
cannot_find_input(event)
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
26
32
|
votes_today = SteemData::AccountOperation.type('vote').where(voter: steem_account).today
|
27
33
|
today_count = votes_today.count
|
28
34
|
author_count = votes_today.where(author: author_name).count
|
@@ -32,27 +38,15 @@ module Cosgrove
|
|
32
38
|
author_count.to_f / today_count
|
33
39
|
end
|
34
40
|
|
35
|
-
post = posts.first
|
36
|
-
|
37
|
-
if post.nil?
|
38
|
-
# Fall back to RPC
|
39
|
-
response = api(:steem).get_content(author_name, permlink)
|
40
|
-
unless response.result.author.empty?
|
41
|
-
post = response.result
|
42
|
-
created = Time.parse(post.created + 'Z')
|
43
|
-
cashout_time = Time.parse(post.cashout_time + 'Z')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
if post.nil?
|
48
|
-
cannot_find_input(event)
|
49
|
-
return
|
50
|
-
end
|
51
|
-
|
52
41
|
created ||= post.created
|
53
42
|
cashout_time ||= post.cashout_time
|
54
43
|
root_post = post.parent_author == ''
|
55
44
|
|
45
|
+
if created.class == String
|
46
|
+
created = Time.parse(created + 'Z')
|
47
|
+
cashout_time = Time.parse(cashout_time + 'Z')
|
48
|
+
end
|
49
|
+
|
56
50
|
nope = if created > 1.minute.ago
|
57
51
|
"Give it a second! It's going to SPACE! Can you give it a second to come back from space?"
|
58
52
|
elsif created > 20.minutes.ago
|
@@ -103,19 +97,63 @@ module Cosgrove
|
|
103
97
|
}
|
104
98
|
|
105
99
|
tx = new_tx :steem
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
100
|
+
tx.operations << vote
|
101
|
+
friendy_error = nil
|
102
|
+
|
103
|
+
loop do
|
104
|
+
begin
|
105
|
+
response = tx.process(true)
|
106
|
+
rescue => e
|
107
|
+
puts "Unable to vote: #{e}"
|
108
|
+
ap e
|
109
|
+
end
|
110
|
+
|
111
|
+
if !!response && !!response.error
|
112
|
+
message = response.error.message
|
113
|
+
if message.to_s =~ /missing required posting authority/
|
114
|
+
friendy_error = "Failed: Check posting key."
|
115
|
+
break
|
116
|
+
elsif message.to_s =~ /You have already voted in a similar way./
|
117
|
+
friendy_error = "Failed: duplicate vote."
|
118
|
+
break
|
119
|
+
elsif message.to_s =~ /Can only vote once every 3 seconds./
|
120
|
+
puts "Retrying: voting too quickly."
|
121
|
+
sleep 3
|
122
|
+
redo
|
123
|
+
elsif message.to_s =~ /Voting weight is too small, please accumulate more voting power or steem power./
|
124
|
+
friendy_error = "Failed: voting weight too small"
|
125
|
+
break
|
126
|
+
elsif message.to_s =~ /unknown key/
|
127
|
+
friendy_error = "Failed: unknown key (testing?)"
|
128
|
+
break
|
129
|
+
elsif message.to_s =~ /tapos_block_summary/
|
130
|
+
puts "Retrying vote/comment: tapos_block_summary (?)"
|
131
|
+
redo
|
132
|
+
elsif message.to_s =~ /now < trx.expiration/
|
133
|
+
puts "Retrying vote/comment: now < trx.expiration (?)"
|
134
|
+
redo
|
135
|
+
elsif message.to_s =~ /signature is not canonical/
|
136
|
+
puts "Retrying vote/comment: signature was not canonical (bug in Radiator?)"
|
137
|
+
redo
|
138
|
+
elsif message.to_s =~ /STEEMIT_UPVOTE_LOCKOUT_HF17/
|
139
|
+
friendy_error = "Failed: upvote lockout (last twelve hours before payout)"
|
140
|
+
break
|
141
|
+
else
|
142
|
+
friendy_error = 'Unable to vote right now. Maybe I already voted on that. Try again later.'
|
143
|
+
ap e
|
144
|
+
ap e.backtrace
|
145
|
+
break
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
break
|
150
|
+
end
|
111
151
|
|
112
|
-
if !!
|
113
|
-
|
152
|
+
if !!friendy_error
|
153
|
+
friendy_error
|
114
154
|
elsif !!response.result.id
|
115
|
-
|
116
|
-
|
117
|
-
# end
|
118
|
-
|
155
|
+
ap response.to_json
|
156
|
+
|
119
157
|
if !!@on_success
|
120
158
|
begin
|
121
159
|
@on_success.call(event, "@#{post.author}/#{post.permlink}")
|