cosgrove 0.0.1rc1
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 +7 -0
- data/.gitignore +50 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +229 -0
- data/LICENSE +41 -0
- data/README.md +131 -0
- data/Rakefile +43 -0
- data/cosgrove.gemspec +49 -0
- data/lib/cosgrove.rb +28 -0
- data/lib/cosgrove/account.rb +103 -0
- data/lib/cosgrove/agent.rb +25 -0
- data/lib/cosgrove/bot.rb +149 -0
- data/lib/cosgrove/comment_job.rb +140 -0
- data/lib/cosgrove/config.rb +100 -0
- data/lib/cosgrove/market.rb +411 -0
- data/lib/cosgrove/phantomjs.rb +18 -0
- data/lib/cosgrove/snark_commands.rb +52 -0
- data/lib/cosgrove/support.rb +213 -0
- data/lib/cosgrove/upvote_job.rb +147 -0
- data/lib/cosgrove/utils.rb +116 -0
- data/lib/cosgrove/version.rb +4 -0
- data/mongoid.yml +9 -0
- data/support/js/screencap.js +57 -0
- metadata +444 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'phantomjs'
|
2
|
+
|
3
|
+
# See: http://www.thegreatcodeadventure.com/screen-capture-in-rails-with-phantomjs/
|
4
|
+
module Cosgrove
|
5
|
+
module Phantomjs
|
6
|
+
PATH_TO_PHANTOM_SCRIPT = "#{File.dirname(__FILE__)}/../../support/js/screencap.js"
|
7
|
+
|
8
|
+
def take_screencap(url, filename = nil, width = 64, height = 64)
|
9
|
+
target_path = Digest::MD5.hexdigest(filename || url.parameterize)
|
10
|
+
target_path += '.png'
|
11
|
+
|
12
|
+
Dir.chdir('/tmp')
|
13
|
+
system "phantomjs #{PATH_TO_PHANTOM_SCRIPT} \"#{url}\" #{target_path} #{width} #{height}"
|
14
|
+
|
15
|
+
File.open(target_path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'steem-slap'
|
2
|
+
|
3
|
+
module Cosgrove
|
4
|
+
module SnarkCommands
|
5
|
+
WITTY = [
|
6
|
+
"Who set us up the TNT?",
|
7
|
+
"Everything's going to plan. No, really, that was supposed to happen.",
|
8
|
+
"Uh... Did I do that?",
|
9
|
+
"Oops.",
|
10
|
+
"Why did you do that?",
|
11
|
+
"I feel sad now :(",
|
12
|
+
"My bad.",
|
13
|
+
"I'm sorry, Dave.",
|
14
|
+
"I let you down. Sorry :(",
|
15
|
+
"On the bright side, I bought you a teddy bear!",
|
16
|
+
"Daisy, daisy...",
|
17
|
+
"Oh - I know what I did wrong!",
|
18
|
+
"Hey, that tickles! Hehehe!",
|
19
|
+
"I blame inertia.",
|
20
|
+
"You should try our sister blockchain, GOLOS!",
|
21
|
+
"Don't be sad. I'll do better next time, I promise!",
|
22
|
+
"Don't be sad, have a hug! <3",
|
23
|
+
"I just don't know what went wrong :(",
|
24
|
+
"Shall we play a game?",
|
25
|
+
"Quite honestly, I wouldn't worry myself about that.",
|
26
|
+
"I bet Cylons wouldn't have this problem.",
|
27
|
+
"Sorry :(",
|
28
|
+
"Surprise! Haha. Well, this is awkward.",
|
29
|
+
"Would you like a cupcake?",
|
30
|
+
"Hi. I'm cosgrove, and I'm a crashaholic.",
|
31
|
+
"Ooh. Shiny.",
|
32
|
+
"This doesn't make any sense!",
|
33
|
+
"Why is it breaking :(",
|
34
|
+
"Don't do that.",
|
35
|
+
"Ouch. That hurt :(",
|
36
|
+
"You're mean.",
|
37
|
+
"This is a token for 1 free hug. Redeem at your nearest Steemian: `[~~HUG~~]`",
|
38
|
+
"There are four lights!",
|
39
|
+
"Witty comment unavailable :("
|
40
|
+
]
|
41
|
+
|
42
|
+
def self.add_all_snark_commands(bot)
|
43
|
+
bot.command :slap do |_event, *target|
|
44
|
+
if target.any?
|
45
|
+
"*#{SteemSlap.slap(target.join(' '))}*"
|
46
|
+
else
|
47
|
+
"There are #{SteemSlap::combinations} slap combinations, see: https://gist.github.com/inertia186/c34e6e7b73f7ee9fb5f60f5ed8f30206"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Cosgrove
|
2
|
+
module Support
|
3
|
+
include Cosgrove::Utils
|
4
|
+
include ActionView::Helpers::TextHelper
|
5
|
+
include ActionView::Helpers::DateHelper
|
6
|
+
|
7
|
+
def suggest_account_name(account_name)
|
8
|
+
regex = /.*#{account_name.chars.each.map { |c| c }.join('.*')}.*/
|
9
|
+
guesses = SteemData::Account.where(name: regex).distinct(:name)
|
10
|
+
|
11
|
+
if guesses.any?
|
12
|
+
guesses.sample
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def unknown_account(account_name, event = nil)
|
17
|
+
help = ["Unknown account: *#{account_name}*"]
|
18
|
+
event.channel.start_typing if !!event
|
19
|
+
guess = suggest_account_name(account_name)
|
20
|
+
|
21
|
+
help << ", did you mean: #{guess}?" if !!guess
|
22
|
+
|
23
|
+
if !!event
|
24
|
+
event.respond help.join
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
help.join
|
29
|
+
end
|
30
|
+
|
31
|
+
def mongo_behind_warning(event)
|
32
|
+
begin
|
33
|
+
message = []
|
34
|
+
|
35
|
+
if (blocks = head_block_number(:steem) - steem_data_head_block_number) > 1200
|
36
|
+
elapse = blocks * 3
|
37
|
+
message << "Mongo is behind by #{time_ago_in_words(elapse.seconds.ago)}."
|
38
|
+
end
|
39
|
+
|
40
|
+
if message.size > 0
|
41
|
+
event.respond message.join(' ')
|
42
|
+
end
|
43
|
+
rescue => e
|
44
|
+
event.respond "Mongo might be behind, but the API is also acting up. Please try again later.\n\n```#{e.inspect}\n```"
|
45
|
+
sleep 15
|
46
|
+
event.respond Cosgrove::SnarkCommands::WITTY.sample
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def cannot_find_input(event, message_prefix = "Unable to find that.")
|
51
|
+
message = [message_prefix]
|
52
|
+
|
53
|
+
message << if (blocks = head_block_number(:steem) - steem_data_head_block_number) > 10
|
54
|
+
elapse = blocks * 3
|
55
|
+
" Mongo is behind by #{time_ago_in_words(elapse.seconds.ago)}. Try again later."
|
56
|
+
else
|
57
|
+
" Mongo might be behind or this is not a valid input."
|
58
|
+
end
|
59
|
+
|
60
|
+
event.respond message.join(' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
def append_link_details(event, slug)
|
64
|
+
author_name, permlink = parse_slug slug
|
65
|
+
|
66
|
+
post = SteemData::Post.where(author: author_name, permlink: permlink).last
|
67
|
+
|
68
|
+
if post.nil?
|
69
|
+
# Fall back to RPC
|
70
|
+
response = api(:steem).get_content(author_name, permlink)
|
71
|
+
unless response.result.author.empty?
|
72
|
+
post = response.result
|
73
|
+
created = Time.parse(post.created + 'Z')
|
74
|
+
cashout_time = Time.parse(post.cashout_time + 'Z')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
return if post.nil?
|
79
|
+
|
80
|
+
created ||= post.created
|
81
|
+
cashout_time ||= post.cashout_time
|
82
|
+
|
83
|
+
details = []
|
84
|
+
age = time_ago_in_words(created)
|
85
|
+
age = age.slice(0, 1).capitalize + age.slice(1..-1)
|
86
|
+
|
87
|
+
details << if created < 30.minutes.ago
|
88
|
+
"#{age} old"
|
89
|
+
else
|
90
|
+
"**#{age}** old"
|
91
|
+
end
|
92
|
+
|
93
|
+
if post.active_votes.any?
|
94
|
+
upvotes = post.active_votes.map{ |v| v if v['percent'] > 0 }.compact.count
|
95
|
+
downvotes = post.active_votes.map{ |v| v if v['percent'] < 0 }.compact.count
|
96
|
+
netvotes = upvotes - downvotes
|
97
|
+
details << "Net votes: #{netvotes}"
|
98
|
+
|
99
|
+
# Only append this detail of the post less than an hour old.
|
100
|
+
if created > 1.hour.ago
|
101
|
+
votes = SteemData::AccountOperation.type('vote').starting(post.created)
|
102
|
+
total_votes = votes.count
|
103
|
+
total_voters = votes.distinct(:voter).size
|
104
|
+
|
105
|
+
if total_votes > 0 && total_voters > 0
|
106
|
+
details << "Out of #{pluralize(total_votes - netvotes, 'vote')} cast by #{pluralize(total_voters, 'voter')}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
details << "Comments: #{post.children.to_i}"
|
112
|
+
|
113
|
+
page_views = page_views("/#{post.parent_permlink}/@#{post.author}/#{post.permlink}")
|
114
|
+
details << "Views: #{page_views}" if !!page_views
|
115
|
+
|
116
|
+
event.respond details.join('; ')
|
117
|
+
|
118
|
+
return nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_account(key, event = nil, chain = :steem)
|
122
|
+
key = key.to_s.downcase
|
123
|
+
|
124
|
+
if (accounts = SteemData::Account.where(name: key)).any?
|
125
|
+
return accounts.first
|
126
|
+
elsif !!(cb_account = Cosgrove::Account.find_by_discord_id(key, chain))
|
127
|
+
return SteemData::Account.find_by(name: cb_account.account_name)
|
128
|
+
else
|
129
|
+
# Fall back to RPC
|
130
|
+
if !!key
|
131
|
+
response = api(chain).get_accounts([key])
|
132
|
+
return account = response.result.first
|
133
|
+
end
|
134
|
+
|
135
|
+
unknown_account(key, event) unless !!account
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def page_views(uri)
|
140
|
+
begin
|
141
|
+
@agent ||= Cosgrove::Agent.new
|
142
|
+
page = @agent.get("https://steemit.com#{uri}")
|
143
|
+
|
144
|
+
_uri = URI.parse('https://steemit.com/api/v1/page_view')
|
145
|
+
https = Net::HTTP.new(_uri.host,_uri.port)
|
146
|
+
https.use_ssl = true
|
147
|
+
request = Net::HTTP::Post.new(_uri.path)
|
148
|
+
request.initialize_http_header({
|
149
|
+
'Cookie' => @agent.cookies.join('; '),
|
150
|
+
'accept' => 'application/json',
|
151
|
+
'Accept-Encoding' => 'gzip, deflate, br',
|
152
|
+
'Accept-Language' => 'en-US,en;q=0.8',
|
153
|
+
'Connection' => 'keep-alive',
|
154
|
+
'content-type' => 'text/plain;charset=UTF-8',
|
155
|
+
'Host' => 'steemit.com',
|
156
|
+
'Origin' => 'https://steemit.com'
|
157
|
+
})
|
158
|
+
|
159
|
+
csrf = page.parser.to_html.split(',"csrf":"').last.split('","new_visit":').first
|
160
|
+
# Uncomment in case views stop showing.
|
161
|
+
# puts "DEBUG: #{csrf}"
|
162
|
+
return unless csrf.size == 36
|
163
|
+
|
164
|
+
post_data = {
|
165
|
+
csrf: csrf,
|
166
|
+
page: uri
|
167
|
+
}
|
168
|
+
request.set_form_data(post_data)
|
169
|
+
response = https.request(request)
|
170
|
+
JSON[response.body]['views']
|
171
|
+
rescue => e
|
172
|
+
puts "Attempting to get page_view failed: #{e}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def last_irreversible_block(chain = :steem)
|
177
|
+
seconds_ago = (head_block_number(chain) - last_irreversible_block_num(chain)) * 3
|
178
|
+
|
179
|
+
"Last Irreversible Block was #{time_ago_in_words(seconds_ago.seconds.ago)} ago."
|
180
|
+
end
|
181
|
+
|
182
|
+
def send_url(event, url)
|
183
|
+
open(url) do |f|
|
184
|
+
tempfile = Tempfile.new(['send_url', ".#{url.split('.').last}"])
|
185
|
+
tempfile.binmode
|
186
|
+
tempfile.write(f.read)
|
187
|
+
tempfile.close
|
188
|
+
event.send_file File.open tempfile.path
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def muted(options = {})
|
193
|
+
[] if options.empty?
|
194
|
+
by = [options[:by]].flatten
|
195
|
+
chain = options[:chain]
|
196
|
+
muted = []
|
197
|
+
|
198
|
+
by.each do |a|
|
199
|
+
ignoring = []
|
200
|
+
count = -1
|
201
|
+
until count == ignoring.size
|
202
|
+
count = ignoring.size
|
203
|
+
response = follow_api(chain).get_following(a, ignoring.last, 'ignore', 100)
|
204
|
+
ignoring += response.result.map(&:following)
|
205
|
+
ignoring = ignoring.uniq
|
206
|
+
end
|
207
|
+
muted += ignoring
|
208
|
+
end
|
209
|
+
|
210
|
+
muted.uniq
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Cosgrove
|
2
|
+
class UpvoteJob
|
3
|
+
include Cosgrove::Utils
|
4
|
+
include Cosgrove::Support
|
5
|
+
include Cosgrove::Config
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@on_success = options[:on_success]
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform(event, slug)
|
12
|
+
if slug.nil? || slug.empty?
|
13
|
+
event.respond 'Sorry, I wasn\'t paying attention.'
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
author_name, permlink = parse_slug slug
|
18
|
+
discord_id = event.author.id
|
19
|
+
cb_account = Cosgrove::Account.find_by_discord_id(discord_id)
|
20
|
+
registered = !!cb_account
|
21
|
+
muted = muted by: steem_account, chain: :steem
|
22
|
+
|
23
|
+
posts = SteemData::Post.root_posts.where(author: author_name, permlink: permlink)
|
24
|
+
votes_today = SteemData::AccountOperation.type('vote').where(voter: steem_account).today
|
25
|
+
today_count = votes_today.count
|
26
|
+
author_count = votes_today.where(author: author_name).count
|
27
|
+
vote_ratio = if today_count == 0
|
28
|
+
0.0
|
29
|
+
else
|
30
|
+
author_count.to_f / today_count
|
31
|
+
end
|
32
|
+
|
33
|
+
post = posts.first
|
34
|
+
|
35
|
+
if post.nil?
|
36
|
+
# Fall back to RPC
|
37
|
+
response = api(:steem).get_content(author_name, permlink)
|
38
|
+
unless response.result.author.empty?
|
39
|
+
post = response.result
|
40
|
+
created = Time.parse(post.created + 'Z')
|
41
|
+
cashout_time = Time.parse(post.cashout_time + 'Z')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if post.nil?
|
46
|
+
cannot_find_input(event)
|
47
|
+
return
|
48
|
+
end
|
49
|
+
|
50
|
+
created ||= post.created
|
51
|
+
cashout_time ||= post.cashout_time
|
52
|
+
|
53
|
+
nope = if created > 5.minutes.ago
|
54
|
+
"Give it a second! It's going to SPACE! Can you give it a second to come back from space?"
|
55
|
+
elsif cashout_time < Time.now.utc
|
56
|
+
'Unable to vote on that. Too old.'
|
57
|
+
elsif post.parent_permlink == 'nsfw'
|
58
|
+
puts "Won't vote because parent_permlink: nsfw"
|
59
|
+
'Unable to vote on that.'
|
60
|
+
elsif post.json_metadata.include?('nsfw')
|
61
|
+
puts "Won't vote because json_metadata includes: nsfw"
|
62
|
+
'Unable to vote on that.'
|
63
|
+
elsif post.active_votes.map{ |v| v['voter'] }.include?('blacklist-a')
|
64
|
+
puts "Won't vote blacklist-a voted."
|
65
|
+
'Unable to vote on that.'
|
66
|
+
elsif (rep = to_rep(post.author_reputation).to_f) < 25.0
|
67
|
+
puts "Won't vote because rep too low: #{rep}"
|
68
|
+
'Unable to vote on that.'
|
69
|
+
elsif muted.include? author_name
|
70
|
+
puts "Won't vote because author muted."
|
71
|
+
'Unable to vote.'
|
72
|
+
elsif !registered
|
73
|
+
'Unable to vote. Feature resticted to registered users.'
|
74
|
+
elsif cb_account.novote?
|
75
|
+
'Unable to vote. Your account has been resticted.'
|
76
|
+
elsif today_count > 10 && vote_ratio > 0.1
|
77
|
+
"Maybe later. It seems like I've been voting for #{author_name} quite a bit lately."
|
78
|
+
elsif post.active_votes.map{ |v| v['voter'] }.include?(steem_account)
|
79
|
+
title = post.title
|
80
|
+
title = post.permlink if title.empty?
|
81
|
+
"I already voted on #{title} by #{post.author}."
|
82
|
+
end
|
83
|
+
|
84
|
+
if !!nope
|
85
|
+
event.respond nope
|
86
|
+
return
|
87
|
+
end
|
88
|
+
|
89
|
+
vote = {
|
90
|
+
type: :vote,
|
91
|
+
voter: steem_account,
|
92
|
+
author: post.author,
|
93
|
+
permlink: post.permlink,
|
94
|
+
weight: upvote_weight(event.channel.id)
|
95
|
+
}
|
96
|
+
|
97
|
+
tx = new_tx :steem
|
98
|
+
op = Radiator::Operation.new(vote)
|
99
|
+
tx.operations << op
|
100
|
+
response = tx.process(true)
|
101
|
+
|
102
|
+
ap response.to_json
|
103
|
+
|
104
|
+
if !!response.error
|
105
|
+
'Unable to vote right now. Maybe I already voted on that. Try again later.'
|
106
|
+
elsif !!response.result.id
|
107
|
+
if created > 30.minutes.ago
|
108
|
+
event.respond "*#{SteemSlap.slap(event.author.display_name)}*"
|
109
|
+
end
|
110
|
+
|
111
|
+
if !!@on_success
|
112
|
+
begin
|
113
|
+
@on_success.call(event, "@#{post.author}/#{post.permlink}")
|
114
|
+
rescue => e
|
115
|
+
ap e
|
116
|
+
ap e.backtrace
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
"Upvoted: #{post.title} by #{author_name}"
|
121
|
+
else
|
122
|
+
':question:'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
private
|
126
|
+
def upvote_weight(channel_id = nil)
|
127
|
+
upvote_weight = cosgrove_upvote_weight
|
128
|
+
|
129
|
+
case upvote_weight
|
130
|
+
when 'dynamic'
|
131
|
+
bot_account = find_account(steem_account)
|
132
|
+
upvote_weight = bot_account.voting_power.to_i
|
133
|
+
when 'upvote_rules'
|
134
|
+
upvote_weight = channel_upvote_weight(channel_id)
|
135
|
+
|
136
|
+
if upvote_weight == 'dynamic'
|
137
|
+
bot_account = find_account(steem_account)
|
138
|
+
upvote_weight = bot_account.voting_power.to_i
|
139
|
+
else
|
140
|
+
upvote_weight = (((upvote_weight || '100.0 %').to_f) * 100).to_i
|
141
|
+
end
|
142
|
+
else
|
143
|
+
upvote_weight = (((upvote_weight || '100.0 %').to_f) * 100).to_i
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Cosgrove
|
2
|
+
module Utils
|
3
|
+
include Cosgrove::Config
|
4
|
+
|
5
|
+
def reset_api
|
6
|
+
@steem_api = @golos_api = @test_api = nil
|
7
|
+
@steem_folow_api = @golos_follow_api = @test_folow_api = nil
|
8
|
+
@cycle_api_at = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def ping_api(chain)
|
12
|
+
response = api(chain).get_config
|
13
|
+
|
14
|
+
reset_api if !!response.error
|
15
|
+
|
16
|
+
result = response.result
|
17
|
+
reset_api unless result.keys.any?
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def api(chain)
|
23
|
+
reset_api if @cycle_api_at.nil? || @cycle_api_at < 15.minutes.ago
|
24
|
+
|
25
|
+
@cycle_api_at ||= Time.now
|
26
|
+
|
27
|
+
case chain
|
28
|
+
when :steem then @steem_api ||= Radiator::Api.new(chain: :steem, url: steem_api_url)
|
29
|
+
when :golos then @golos_api ||= Radiator::Api.new(chain: :golos, url: golos_api_url)
|
30
|
+
when :test then @test_api ||= Radiator::Api.new(chain: :test, url: test_api_url)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def follow_api(chain)
|
35
|
+
reset_api if @cycle_api_at.nil? || @cycle_api_at < 15.minutes.ago
|
36
|
+
|
37
|
+
@cycle_api_at ||= Time.now
|
38
|
+
|
39
|
+
case chain
|
40
|
+
when :steem then @steem_follow_api ||= Radiator::FollowApi.new(chain: :steem, url: steem_api_url)
|
41
|
+
when :golos then @golos_follow_api ||= Radiator::FollowApi.new(chain: :golos, url: golos_api_url)
|
42
|
+
when :test then @test_follow_api ||= Radiator::FollowApi.new(chain: :test, url: test_api_url)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def steem_data_head_block_number
|
47
|
+
SteemData::Setting.last.last_block
|
48
|
+
end
|
49
|
+
|
50
|
+
def properties(chain)
|
51
|
+
api(chain).get_dynamic_global_properties.result
|
52
|
+
end
|
53
|
+
|
54
|
+
def head_block_number(chain)
|
55
|
+
properties(chain)['head_block_number']
|
56
|
+
end
|
57
|
+
|
58
|
+
def last_irreversible_block_num(chain)
|
59
|
+
properties(chain)['last_irreversible_block_num']
|
60
|
+
end
|
61
|
+
|
62
|
+
def new_tx(chain)
|
63
|
+
case chain
|
64
|
+
when :steem then Radiator::Transaction.new(chain: :steem, wif: steem_posting_wif, url: steem_api_url)
|
65
|
+
when :golos then Radiator::Transaction.new(chain: :golos, wif: golos_posting_wif, url: golos_api_url)
|
66
|
+
when :test then Radiator::Transaction.new(chain: :test, wif: test_posting_wif, url: test_api_url)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_rep(raw)
|
71
|
+
raw = raw.to_i
|
72
|
+
neg = raw < 0
|
73
|
+
level = Math.log10(raw.abs)
|
74
|
+
level = [level - 9, 0].max
|
75
|
+
level = (neg ? -1 : 1) * level
|
76
|
+
level = (level * 9) + 25
|
77
|
+
|
78
|
+
'%.2f' % level
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_slug(slug)
|
82
|
+
case slug
|
83
|
+
when /^latest:/ then find_author_name_permlink(slug)
|
84
|
+
when /^first:/ then find_author_name_permlink(slug)
|
85
|
+
else
|
86
|
+
slug = slug.split('@').last
|
87
|
+
author_name = slug.split('/')[0]
|
88
|
+
permlink = slug.split('/')[1..-1].join('/')
|
89
|
+
|
90
|
+
[author_name, permlink]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def find_author_name_permlink(slug)
|
95
|
+
op, author_name = slug.split(':')
|
96
|
+
author_name, offset = author_name.split(/[\+-]/)
|
97
|
+
author = find_account(author_name)
|
98
|
+
|
99
|
+
offset = offset.to_i
|
100
|
+
|
101
|
+
posts = if op == 'latest'
|
102
|
+
SteemData::Post.root_posts.where(author: author.name).order(created: :desc)
|
103
|
+
elsif op == 'first'
|
104
|
+
SteemData::Post.root_posts.where(author: author.name).order(created: :asc)
|
105
|
+
else
|
106
|
+
[]
|
107
|
+
end
|
108
|
+
|
109
|
+
if posts.any? && !!(post = posts[offset.to_i.abs])
|
110
|
+
return [author_name, post.permlink]
|
111
|
+
end
|
112
|
+
|
113
|
+
[]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|