cosgrove 0.0.2 → 0.0.3rc1
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 +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}")
|