mixin_bot 0.3.5 → 0.3.10

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8282148099d4fa3128ae77f40fbefbd5712edeacebbd10fc286055b463427f40
4
- data.tar.gz: d8e8b3511fb866dd76004534d2629f6edb362d82177d8a717a5f7a797c532b8e
3
+ metadata.gz: cd8f989ccf2f759f7a9498fbacb7ce8b2f1652c254b08d9c021de11ff2814ecc
4
+ data.tar.gz: e3da8ada69570970fd7277058f0f55b5b05696dde767be56190eef4a7bab8a21
5
5
  SHA512:
6
- metadata.gz: ac472ad54cc28559dec43283aa12691984fb5dd4f1d4dc5c2356b72716cfedd754d209b4b37f7ca4986408e7155523a2a41caeeec140c9ab6dcd00704e7b0d2e
7
- data.tar.gz: 414a6e52690819775032c7abd45d2cc4e885bc0d12a195ba87e4d5bd945ebac1eb8ffad7b458d3f3faf7cbfba5cccc157b5c5f6cab1517685dc28e15041389fe
6
+ metadata.gz: c8fc866de489a28d5566ae5711a42c6635ab62a11c8789b744728d7eac2e428af451114d80f8383d2ddb1d9997e92a84ca9bffe24f9eee3e3f1feadcf768b002
7
+ data.tar.gz: 7aeea158f3c1f142b82e9ffbc26647aa30d8c8dfe0712eede9417cc7875786d95cef4cdbe617f6f5f6e6f90092a1fb9934a64fe9cfbdef89261b008b180262d4
data/lib/mixin_bot.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # third-party dependencies
3
4
  require 'English'
4
5
  require 'base64'
5
6
  require 'digest'
@@ -10,6 +11,8 @@ require 'msgpack'
10
11
  require 'open3'
11
12
  require 'openssl'
12
13
  require 'rbnacl'
14
+ require 'sha3'
15
+
13
16
  require_relative './mixin_bot/api'
14
17
  require_relative './mixin_bot/cli'
15
18
  require_relative './mixin_bot/version'
@@ -22,4 +25,10 @@ module MixinBot
22
25
  def self.api
23
26
  @api ||= MixinBot::API.new
24
27
  end
28
+
29
+ class HttpError < StandardError; end
30
+ class RequestError < StandardError; end
31
+ class ResponseError < StandardError; end
32
+ class UnauthorizedError < StandardError; end
33
+ class ForbiddenError < StandardError; end
25
34
  end
data/lib/mixin_bot/api.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './client'
4
- require_relative './errors'
5
4
  require_relative './api/app'
5
+ require_relative './api/asset'
6
6
  require_relative './api/attachment'
7
7
  require_relative './api/auth'
8
8
  require_relative './api/blaze'
@@ -54,6 +54,7 @@ module MixinBot
54
54
  end
55
55
 
56
56
  include MixinBot::API::App
57
+ include MixinBot::API::Asset
57
58
  include MixinBot::API::Attachment
58
59
  include MixinBot::API::Auth
59
60
  include MixinBot::API::Blaze
@@ -6,7 +6,7 @@ module MixinBot
6
6
  def add_favorite_app(app_id, access_token: nil)
7
7
  path = format('/apps/%<id>s/favorite', id: app_id)
8
8
 
9
- access_token ||= access_token('POST', path, {})
9
+ access_token ||= access_token('POST', path)
10
10
  authorization = format('Bearer %<access_token>s', access_token: access_token)
11
11
  client.post(path, headers: { 'Authorization': authorization })
12
12
  end
@@ -14,7 +14,7 @@ module MixinBot
14
14
  def remove_favorite_app(app_id, access_token: nil)
15
15
  path = format('/apps/%<id>s/unfavorite', id: app_id)
16
16
 
17
- access_token ||= access_token('POST', path, '')
17
+ access_token ||= access_token('POST', path)
18
18
  authorization = format('Bearer %<access_token>s', access_token: access_token)
19
19
  client.post(path, headers: { 'Authorization': authorization })
20
20
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module Asset
6
+ # https://developers.mixin.one/api/alpha-mixin-network/read-assets/
7
+ def assets(access_token: nil)
8
+ path = '/assets'
9
+ access_token ||= access_token('GET', path, '')
10
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
11
+ client.get(path, headers: { 'Authorization': authorization })
12
+ end
13
+ alias read_assets assets
14
+
15
+ # https://developers.mixin.one/api/alpha-mixin-network/read-asset/
16
+ def asset(asset_id, access_token: nil)
17
+ path = format('/assets/%<asset_id>s', asset_id: asset_id)
18
+ access_token ||= access_token('GET', path, '')
19
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
20
+ client.get(path, headers: { 'Authorization': authorization })
21
+ end
22
+ alias read_asset asset
23
+
24
+ # https://developers.mixin.one/document/wallet/api/ticker
25
+ def ticker(asset_id, offset, access_token: nil)
26
+ offset = DateTime.rfc3339 offset if offset.is_a? String
27
+ offset = offset.rfc3339 if offset.is_a?(DateTime) || offset.is_a?(Time)
28
+ path = format('/ticker?asset=%<asset_id>s&offset=%<offset>s', asset_id: asset_id, offset: offset)
29
+ access_token ||= access_token('GET', path, '')
30
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
31
+ client.get(path, headers: { 'Authorization': authorization })
32
+ end
33
+ alias read_ticker ticker
34
+ end
35
+ end
36
+ end
@@ -4,7 +4,7 @@ module MixinBot
4
4
  class API
5
5
  module Auth
6
6
  def access_token(method, uri, body = '', exp_in: 600, scp: 'FULL')
7
- sig = Digest::SHA256.hexdigest(method + uri + body)
7
+ sig = Digest::SHA256.hexdigest(method + uri + body.to_s)
8
8
  iat = Time.now.utc.to_i
9
9
  exp = (Time.now.utc + exp_in).to_i
10
10
  jti = SecureRandom.uuid
@@ -3,37 +3,126 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Conversation
6
- def read_conversation(conversation_id)
6
+ def conversation(conversation_id)
7
7
  path = format('/conversations/%<conversation_id>s', conversation_id: conversation_id)
8
8
  access_token ||= access_token('GET', path, '')
9
9
  authorization = format('Bearer %<access_token>s', access_token: access_token)
10
10
  client.get(path, headers: { 'Authorization': authorization })
11
11
  end
12
+ alias read_conversation conversation
12
13
 
13
- def read_conversation_by_user_id(user_id)
14
+ def conversation_by_user_id(user_id)
14
15
  conversation_id = unique_conversation_id(user_id)
15
16
  read_conversation(conversation_id)
16
17
  end
18
+ alias read_conversation_by_user_id conversation_by_user_id
17
19
 
18
- def create_contact_conversation(user_id)
20
+ def create_conversation(category:, conversation_id:, participants:, name: nil, access_token: nil)
19
21
  path = '/conversations'
20
22
  payload = {
23
+ category: category,
24
+ conversation_id: conversation_id || SecureRandom.uuid,
25
+ name: name,
26
+ participants: participants
27
+ }
28
+
29
+ access_token ||= access_token('POST', path, payload.to_json)
30
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
31
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
32
+ end
33
+
34
+ def create_group_conversation(user_ids:, name:, conversation_id: nil, access_token: nil)
35
+ create_conversation(
36
+ category: 'GROUP',
37
+ conversation_id: conversation_id,
38
+ name: name,
39
+ participants: user_ids.map(&->(participant) { { user_id: participant } }),
40
+ access_token: access_token
41
+ )
42
+ end
43
+
44
+ def create_contact_conversation(user_id, access_token: nil)
45
+ create_conversation(
21
46
  category: 'CONTACT',
22
47
  conversation_id: unique_conversation_id(user_id),
23
48
  participants: [
24
49
  {
25
- action: 'ADD',
26
- role: '',
27
50
  user_id: user_id
28
51
  }
29
- ]
52
+ ],
53
+ access_token: access_token
54
+ )
55
+ end
56
+
57
+ def update_group_conversation_name(name:, conversation_id:, access_token: nil)
58
+ path = format('/conversations/%<id>s', id: conversation_id)
59
+ payload = {
60
+ name: name
61
+ }
62
+
63
+ access_token ||= access_token('POST', path, payload.to_json)
64
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
65
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
66
+ end
67
+
68
+ def update_group_conversation_announcement(announcement:, conversation_id:, access_token: nil)
69
+ path = format('/conversations/%<id>s', id: conversation_id)
70
+ payload = {
71
+ announcement: announcement
30
72
  }
73
+
74
+ access_token ||= access_token('POST', path, payload.to_json)
75
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
76
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
77
+ end
78
+
79
+ # participants = [{ user_id: "" }]
80
+ def add_conversation_participants(conversation_id:, user_ids:, access_token: nil)
81
+ path = format('/conversations/%<id>s/participants/ADD', id: conversation_id)
82
+ payload = user_ids.map(&->(participant) { { user_id: participant } })
83
+
84
+ access_token ||= access_token('POST', path, payload.to_json)
85
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
86
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
87
+ end
88
+
89
+ # participants = [{ user_id: "" }]
90
+ def remove_conversation_participants(conversation_id:, user_ids:, access_token: nil)
91
+ path = format('/conversations/%<id>s/participants/REMOVE', id: conversation_id)
92
+ payload = user_ids.map(&->(participant) { { user_id: participant } })
93
+
94
+ access_token ||= access_token('POST', path, payload.to_json)
95
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
96
+ client.post(path, headers: { 'Authorization': authorization }, json: payload)
97
+ end
98
+
99
+ def exit_conversation(conversation_id, access_token: nil)
100
+ path = format('/conversations/%<id>s/exit', id: conversation_id)
101
+
102
+ access_token ||= access_token('POST', path)
103
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
104
+ client.post(path, headers: { 'Authorization': authorization })
105
+ end
106
+
107
+ def rotate_conversation(conversation_id, access_token: nil)
108
+ path = format('/conversations/%<id>s/rotate', id: conversation_id)
109
+
110
+ access_token ||= access_token('POST', path)
111
+ authorization = format('Bearer %<access_token>s', access_token: access_token)
112
+ client.post(path, headers: { 'Authorization': authorization })
113
+ end
114
+
115
+ # participants = [{ user_id: "", role: "ADMIN" }]
116
+ def update_conversation_participants_role(conversation_id:, participants:, access_token: nil)
117
+ path = format('/conversations/%<id>s/participants/ROLE', id: conversation_id)
118
+ payload = participants
119
+
31
120
  access_token ||= access_token('POST', path, payload.to_json)
32
121
  authorization = format('Bearer %<access_token>s', access_token: access_token)
33
122
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
34
123
  end
35
124
 
36
- def unique_conversation_id(user_id, opponent_id = nil)
125
+ def unique_uuid(user_id, opponent_id = nil)
37
126
  opponent_id ||= client_id
38
127
  md5 = Digest::MD5.new
39
128
  md5 << [user_id, opponent_id].min
@@ -53,6 +142,7 @@ module MixinBot
53
142
  fifth: hex[20..]
54
143
  )
55
144
  end
145
+ alias unique_conversation_id unique_uuid
56
146
  end
57
147
  end
58
148
  end
@@ -4,12 +4,13 @@ module MixinBot
4
4
  class API
5
5
  module Me
6
6
  # https://developers.mixin.one/api/beta-mixin-message/read-profile/
7
- def read_me(access_token: nil)
7
+ def me(access_token: nil)
8
8
  path = '/me'
9
9
  access_token ||= access_token('GET', path, '')
10
10
  authorization = format('Bearer %<access_token>s', access_token: access_token)
11
11
  client.get(path, headers: { 'Authorization': authorization })
12
12
  end
13
+ alias read_me me
13
14
 
14
15
  # https://developers.mixin.one/api/beta-mixin-message/update-profile/
15
16
  # avatar_base64:
@@ -25,29 +26,14 @@ module MixinBot
25
26
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
26
27
  end
27
28
 
28
- # https://developers.mixin.one/api/alpha-mixin-network/read-assets/
29
- def read_assets(access_token: nil)
30
- path = '/assets'
31
- access_token ||= access_token('GET', path, '')
32
- authorization = format('Bearer %<access_token>s', access_token: access_token)
33
- client.get(path, headers: { 'Authorization': authorization })
34
- end
35
-
36
- # https://developers.mixin.one/api/alpha-mixin-network/read-asset/
37
- def read_asset(asset_id, access_token: nil)
38
- path = format('/assets/%<asset_id>s', asset_id: asset_id)
39
- access_token ||= access_token('GET', path, '')
40
- authorization = format('Bearer %<access_token>s', access_token: access_token)
41
- client.get(path, headers: { 'Authorization': authorization })
42
- end
43
-
44
29
  # https://developers.mixin.one/api/beta-mixin-message/friends/
45
- def read_friends(access_token: nil)
30
+ def friends(access_token: nil)
46
31
  path = '/friends'
47
32
  access_token ||= access_token('GET', path, '')
48
33
  authorization = format('Bearer %<access_token>s', access_token: access_token)
49
34
  client.get(path, headers: { 'Authorization': authorization })
50
35
  end
36
+ alias read_friends friends
51
37
  end
52
38
  end
53
39
  end
@@ -28,26 +28,28 @@ module MixinBot
28
28
  # "signed_tx":""
29
29
  # }
30
30
  # ]}
31
- def multisigs(limit: 100, offset: nil, access_token: nil)
32
- path = format('/multisigs?limit=%<limit>s&offset=%<offset>s', limit: limit, offset: offset)
31
+ def outputs(**kwargs)
32
+ limit = kwargs[:limit] || 100
33
+ offset = kwargs[:offset]
34
+ state = kwargs[:state]
35
+ members = kwargs[:members]
36
+ threshold = kwargs[:threshold]
37
+ access_token = kwargs[:access_token]
38
+ members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
39
+
40
+ path = format(
41
+ '/multisigs/outputs?limit=%<limit>s&offset=%<offset>s&state=%<state>s&members=%<members>s&threshold=%<threshold>s',
42
+ limit: limit,
43
+ offset: offset,
44
+ state: state,
45
+ members: members,
46
+ threshold: threshold
47
+ )
33
48
  access_token ||= access_token('GET', path, '')
34
49
  authorization = format('Bearer %<access_token>s', access_token: access_token)
35
50
  client.get(path, headers: { 'Authorization': authorization })
36
51
  end
37
-
38
- def all_multisigs(utxos: [], offset: nil, access_token: nil)
39
- res = multisigs(limit: 100, offset: offset, access_token: access_token)
40
-
41
- return [] if res['data'].nil?
42
-
43
- utxos += res['data']
44
-
45
- if res['data'].length < 100
46
- utxos
47
- else
48
- all_multisigs(utxos: utxos, offset: utxos[-1]['created_at'], access_token: access_token)
49
- end
50
- end
52
+ alias multisigs outputs
51
53
 
52
54
  def create_output(receivers:, index:, access_token: nil)
53
55
  path = '/outputs'
@@ -64,7 +66,7 @@ module MixinBot
64
66
  # create a request for multi sign
65
67
  # for now, raw(RAW-TRANSACTION-HEX) can only be generated by Mixin SDK of Golang or Javascript
66
68
  def create_sign_multisig_request(raw, access_token: nil)
67
- path = '/multisigs'
69
+ path = '/multisigs/requests'
68
70
  payload = {
69
71
  action: 'sign',
70
72
  raw: raw
@@ -77,7 +79,7 @@ module MixinBot
77
79
  # transfer from the multisig address
78
80
  # create a request for unlock a multi-sign
79
81
  def create_unlock_multisig_request(raw, access_token: nil)
80
- path = '/multisigs'
82
+ path = '/multisigs/requests'
81
83
  payload = {
82
84
  action: 'unlock',
83
85
  raw: raw
@@ -88,7 +90,7 @@ module MixinBot
88
90
  end
89
91
 
90
92
  def sign_multisig_request(request_id, pin)
91
- path = format('/multisigs/%<request_id>s/sign', request_id: request_id)
93
+ path = format('/multisigs/requests/%<request_id>s/sign', request_id: request_id)
92
94
  payload = {
93
95
  pin: encrypt_pin(pin)
94
96
  }
@@ -98,7 +100,7 @@ module MixinBot
98
100
  end
99
101
 
100
102
  def unlock_multisig_request(request_id, pin)
101
- path = format('/multisigs/%<request_id>s/unlock', request_id: request_id)
103
+ path = format('/multisigs/requests/%<request_id>s/unlock', request_id: request_id)
102
104
  payload = {
103
105
  pin: encrypt_pin(pin)
104
106
  }
@@ -108,7 +110,7 @@ module MixinBot
108
110
  end
109
111
 
110
112
  def cancel_multisig_request(request_id, pin)
111
- path = format('/multisigs/%<request_id>s/cancel', request_id: request_id)
113
+ path = format('/multisigs/requests/%<request_id>s/cancel', request_id: request_id)
112
114
  payload = {
113
115
  pin: encrypt_pin(pin)
114
116
  }
@@ -119,19 +121,19 @@ module MixinBot
119
121
 
120
122
  # pay to the multisig address
121
123
  # used for create multisig payment code_id
122
- def create_multisig_payment(params)
124
+ def create_multisig_payment(**kwargs)
123
125
  path = '/payments'
124
126
  payload = {
125
- asset_id: params[:asset_id],
126
- amount: params[:amount].to_s,
127
- trace_id: params[:trace_id] || SecureRandom.uuid,
128
- memo: params[:memo],
127
+ asset_id: kwargs[:asset_id],
128
+ amount: kwargs[:amount].to_s,
129
+ trace_id: kwargs[:trace_id] || SecureRandom.uuid,
130
+ memo: kwargs[:memo],
129
131
  opponent_multisig: {
130
- receivers: params[:receivers],
131
- threshold: params[:threshold]
132
+ receivers: kwargs[:receivers],
133
+ threshold: kwargs[:threshold]
132
134
  }
133
135
  }
134
- access_token = params[:access_token]
136
+ access_token = kwargs[:access_token]
135
137
  access_token ||= access_token('POST', path, payload.to_json)
136
138
  authorization = format('Bearer %<access_token>s', access_token: access_token)
137
139
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
@@ -165,76 +167,42 @@ module MixinBot
165
167
  "fffe#{s}"
166
168
  end
167
169
 
168
- # filter utxo by members, asset_id and threshold
169
- def filter_utxos(params)
170
- utxos = all_multisigs(access_token: params[:access_token])
171
-
172
- unless params[:members].nil?
173
- utxos = utxos.filter(
174
- &lambda { |utxo|
175
- utxo['members'].sort == params[:members].sort
176
- }
177
- )
178
- end
179
-
180
- unless params[:asset_id].nil?
181
- utxos = utxos.filter(
182
- &lambda { |utxo|
183
- utxo['asset_id'] == params[:asset_id]
184
- }
185
- )
186
- end
187
-
188
- unless params[:threshold].nil?
189
- utxos = utxos.filter(
190
- &lambda { |utxo|
191
- utxo['threshold'] == params[:threshold]
192
- }
193
- )
194
- end
195
-
196
- unless params[:state].nil?
197
- utxos = utxos.filter(
198
- &lambda { |utxo|
199
- utxo['state'] == params[:state]
200
- }
201
- )
202
- end
203
-
204
- utxos
205
- end
206
-
207
- # params:
170
+ # kwargs:
208
171
  # {
209
172
  # senders: [ uuid ],
210
173
  # receivers: [ uuid ],
211
174
  # threshold: integer,
212
175
  # asset_id: uuid,
213
- # asset_mixin_id: string,
214
176
  # amount: string / float,
215
177
  # memo: string,
216
178
  # }
217
- def build_raw_transaction(params)
218
- senders = params[:senders]
219
- receivers = params[:receivers]
220
- asset_id = params[:asset_id]
221
- asset_mixin_id = params[:asset_mixin_id]
222
- amount = params[:amount]
223
- memo = params[:memo]
224
- threshold = params[:threshold]
225
- access_token = params[:access_token]
226
- utxos = params[:utxos]
179
+ RAW_TRANSACTION_ARGUMENTS = %i[senders receivers amount threshold asset_id].freeze
180
+ def build_raw_transaction(**kwargs)
181
+ raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
182
+
183
+ senders = kwargs[:senders]
184
+ receivers = kwargs[:receivers]
185
+ amount = kwargs[:amount]
186
+ threshold = kwargs[:threshold]
187
+ asset_id = kwargs[:asset_id]
188
+ utxos = kwargs[:utxos]
189
+ memo = kwargs[:memo]
190
+ access_token = kwargs[:access_token]
227
191
 
228
192
  raise 'access_token required!' if access_token.nil? && !senders.include?(client_id)
229
193
 
230
- # default to use all unspent utxo
231
- utxos ||= filter_utxos(
194
+ # default to use all(first 100) unspent utxo
195
+ utxos ||= multisigs(
232
196
  members: senders,
233
- asset_id: asset_id,
234
197
  threshold: threshold,
235
198
  state: 'unspent',
236
199
  access_token: access_token
200
+ ).filter(
201
+ &lambda { |utxo|
202
+ utxo['asset_id'] == kwargs[:asset_id]
203
+ }
237
204
  )
205
+
238
206
  amount = amount.to_f.round(8)
239
207
  input_amount = utxos.map(
240
208
  &lambda { |utxo|
@@ -281,7 +249,7 @@ module MixinBot
281
249
  extra = Digest.hexencode memo.to_s.slice(0, 140)
282
250
  tx = {
283
251
  version: 1,
284
- asset: asset_mixin_id,
252
+ asset: SHA3::Digest::SHA256.hexdigest(asset_id),
285
253
  inputs: inputs,
286
254
  outputs: outputs,
287
255
  extra: extra
@@ -3,7 +3,7 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Snapshot
6
- def read_network_snapshots(options = {})
6
+ def network_snapshots(options = {})
7
7
  path = format(
8
8
  '/network/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s&order=%<order>s',
9
9
  limit: options[:limit],
@@ -16,8 +16,9 @@ module MixinBot
16
16
  authorization = format('Bearer %<access_token>s', access_token: access_token)
17
17
  client.get(path, headers: { 'Authorization': authorization })
18
18
  end
19
+ alias read_network_snapshots network_snapshots
19
20
 
20
- def read_snapshots(options = {})
21
+ def snapshots(options = {})
21
22
  path = format(
22
23
  '/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s',
23
24
  limit: options[:limit],
@@ -29,14 +30,16 @@ module MixinBot
29
30
  authorization = format('Bearer %<access_token>s', access_token: access_token)
30
31
  client.get(path, headers: { 'Authorization': authorization })
31
32
  end
33
+ alias read_snapshots snapshots
32
34
 
33
- def read_network_snapshot(snapshot_id, options = {})
35
+ def network_snapshot(snapshot_id, options = {})
34
36
  path = format('/network/snapshots/%<snapshot_id>s', snapshot_id: snapshot_id)
35
37
 
36
38
  access_token = options[:access_token] || access_token('GET', path)
37
39
  authorization = format('Bearer %<access_token>s', access_token: access_token)
38
40
  client.get(path, headers: { 'Authorization': authorization })
39
41
  end
42
+ alias read_network_snapshot network_snapshot
40
43
  end
41
44
  end
42
45
  end
@@ -3,7 +3,10 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Transfer
6
- def create_transfer(pin, options, access_token: nil)
6
+ TRANSFER_ARGUMENTS = %i[asset_id opponent_id amount].freeze
7
+ def create_transfer(pin, options = {})
8
+ raise ArgumentError, "#{TRANSFER_ARGUMENTS.join(', ')} are needed for create transfer" unless TRANSFER_ARGUMENTS.all? { |param| options.keys.include? param }
9
+
7
10
  asset_id = options[:asset_id]
8
11
  opponent_id = options[:opponent_id]
9
12
  amount = options[:amount]
@@ -21,17 +24,19 @@ module MixinBot
21
24
  memo: memo
22
25
  }
23
26
 
27
+ access_token = options[:access_token]
24
28
  access_token ||= access_token('POST', path, payload.to_json)
25
29
  authorization = format('Bearer %<access_token>s', access_token: access_token)
26
30
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
27
31
  end
28
32
 
29
- def read_transfer(trace_id, access_token: nil)
33
+ def transfer(trace_id, access_token: nil)
30
34
  path = format('/transfers/trace/%<trace_id>s', trace_id: trace_id)
31
35
  access_token ||= access_token('GET', path, '')
32
36
  authorization = format('Bearer %<access_token>s', access_token: access_token)
33
37
  client.get(path, headers: { 'Authorization': authorization })
34
38
  end
39
+ alias read_transfer transfer
35
40
  end
36
41
  end
37
42
  end
@@ -29,17 +29,42 @@ module MixinBot
29
29
  begin
30
30
  response = HTTP.timeout(connect: 5, write: 5, read: 5).request(verb, uri, options)
31
31
  rescue HTTP::Error => e
32
- raise Errors::HttpError, e.message
32
+ raise HttpError, e.message
33
33
  end
34
34
 
35
- raise Errors::APIError.new(nil, response.to_s) unless response.status.success?
35
+ raise RequestError, response.to_s unless response.status.success?
36
36
 
37
37
  parse_response(response) do |parse_as, result|
38
38
  case parse_as
39
39
  when :json
40
- break result if result[:errcode].nil? || result[:errcode].zero?
40
+ if result['error'].nil?
41
+ result.merge! result['data'] if result['data'].is_a? Hash
42
+ break result
43
+ end
41
44
 
42
- raise Errors::APIError.new(result[:errcode], result[:errmsg])
45
+ errmsg = "errcode: #{result['error']['code']}, errmsg: #{result['error']['description']}"
46
+
47
+ # status code description
48
+ # 202 400 The request body can’t be pasred as valid data.
49
+ # 202 401 Unauthorized.
50
+ # 202 403 Forbidden.
51
+ # 202 404 The endpoint is not found.
52
+ # 202 429 Too Many Requests.
53
+ # 202 10006 App update required.
54
+ # 202 20116 The group chat is full.
55
+ # 500 500 Internal Server Error.
56
+ # 500 7000 Blaze server error.
57
+ # 500 7001 The blaze operation timeout.
58
+ case result['error']['code']
59
+ when 401
60
+ raise UnauthorizedError, errmsg
61
+ when 403, 20116
62
+ raise ForbiddenError, errmsg
63
+ when 400, 404, 429, 10006, 20133, 500, 7000, 7001
64
+ raise ResponseError, errmsg
65
+ else
66
+ raise ResponseError, errmsg
67
+ end
43
68
  else
44
69
  result
45
70
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '0.3.5'
4
+ VERSION = '0.3.10'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mixin_bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - an-lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-05 00:00:00.000000000 Z
11
+ date: 2021-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_print
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '7.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sha3
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: thor
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,20 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '1.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: pry
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 0.13.1
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 0.13.1
139
167
  - !ruby/object:Gem::Dependency
140
168
  name: rake
141
169
  requirement: !ruby/object:Gem::Requirement
@@ -219,6 +247,7 @@ files:
219
247
  - lib/mixin_bot.rb
220
248
  - lib/mixin_bot/api.rb
221
249
  - lib/mixin_bot/api/app.rb
250
+ - lib/mixin_bot/api/asset.rb
222
251
  - lib/mixin_bot/api/attachment.rb
223
252
  - lib/mixin_bot/api/auth.rb
224
253
  - lib/mixin_bot/api/blaze.rb
@@ -237,7 +266,6 @@ files:
237
266
  - lib/mixin_bot/cli/multisig.rb
238
267
  - lib/mixin_bot/cli/node.rb
239
268
  - lib/mixin_bot/client.rb
240
- - lib/mixin_bot/errors.rb
241
269
  - lib/mixin_bot/version.rb
242
270
  homepage: https://github.com/an-lee/mixin_bot
243
271
  licenses:
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MixinBot
4
- module Errors
5
- # 通用异常
6
- Error = Class.new(StandardError)
7
-
8
- # HTTP 异常,比如请求超时等
9
- HttpError = Class.new(Error)
10
-
11
- # API 异常,比如返回失败状态码
12
- class APIError < Error
13
- attr_reader :errcode, :errmsg
14
-
15
- def initialize(errcode, errmsg)
16
- @errcode = errcode
17
- @errmsg = errmsg
18
-
19
- super(format('[%<errcode>s]: %<errmsg>s', errcode: @errcode, errmsg: @errmsg))
20
- end
21
- end
22
- end
23
- end