mixin_bot 0.12.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mixin_bot/api/address.rb +21 -0
  3. data/lib/mixin_bot/api/app.rb +5 -11
  4. data/lib/mixin_bot/api/asset.rb +9 -16
  5. data/lib/mixin_bot/api/attachment.rb +27 -22
  6. data/lib/mixin_bot/api/auth.rb +29 -51
  7. data/lib/mixin_bot/api/blaze.rb +4 -3
  8. data/lib/mixin_bot/api/collectible.rb +60 -58
  9. data/lib/mixin_bot/api/conversation.rb +29 -49
  10. data/lib/mixin_bot/api/encrypted_message.rb +17 -17
  11. data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
  12. data/lib/mixin_bot/api/legacy_output.rb +50 -0
  13. data/lib/mixin_bot/api/legacy_payment.rb +31 -0
  14. data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
  15. data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
  16. data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
  17. data/lib/mixin_bot/api/me.rb +13 -17
  18. data/lib/mixin_bot/api/message.rb +13 -10
  19. data/lib/mixin_bot/api/multisig.rb +16 -221
  20. data/lib/mixin_bot/api/output.rb +46 -0
  21. data/lib/mixin_bot/api/payment.rb +9 -20
  22. data/lib/mixin_bot/api/pin.rb +57 -65
  23. data/lib/mixin_bot/api/rpc.rb +9 -11
  24. data/lib/mixin_bot/api/snapshot.rb +15 -29
  25. data/lib/mixin_bot/api/tip.rb +43 -0
  26. data/lib/mixin_bot/api/transaction.rb +184 -60
  27. data/lib/mixin_bot/api/transfer.rb +64 -32
  28. data/lib/mixin_bot/api/user.rb +83 -53
  29. data/lib/mixin_bot/api/withdraw.rb +52 -53
  30. data/lib/mixin_bot/api.rb +78 -45
  31. data/lib/mixin_bot/cli/api.rb +149 -5
  32. data/lib/mixin_bot/cli/utils.rb +14 -4
  33. data/lib/mixin_bot/cli.rb +13 -10
  34. data/lib/mixin_bot/client.rb +76 -127
  35. data/lib/mixin_bot/configuration.rb +98 -0
  36. data/lib/mixin_bot/nfo.rb +174 -0
  37. data/lib/mixin_bot/transaction.rb +505 -0
  38. data/lib/mixin_bot/utils/address.rb +108 -0
  39. data/lib/mixin_bot/utils/crypto.rb +182 -0
  40. data/lib/mixin_bot/utils/decoder.rb +58 -0
  41. data/lib/mixin_bot/utils/encoder.rb +63 -0
  42. data/lib/mixin_bot/utils.rb +8 -109
  43. data/lib/mixin_bot/uuid.rb +41 -0
  44. data/lib/mixin_bot/version.rb +1 -1
  45. data/lib/mixin_bot.rb +39 -14
  46. data/lib/mvm/bridge.rb +2 -19
  47. data/lib/mvm/client.rb +11 -33
  48. data/lib/mvm/nft.rb +4 -4
  49. data/lib/mvm/registry.rb +9 -9
  50. data/lib/mvm/scan.rb +3 -5
  51. data/lib/mvm.rb +5 -6
  52. metadata +101 -44
  53. data/lib/mixin_bot/utils/nfo.rb +0 -176
  54. data/lib/mixin_bot/utils/transaction.rb +0 -478
  55. data/lib/mixin_bot/utils/uuid.rb +0 -43
@@ -90,11 +90,11 @@ module MixinBot
90
90
  category: options[:category],
91
91
  quote_message_id: options[:quote_message_id],
92
92
  message_id: options[:message_id] || SecureRandom.uuid,
93
- data_base64: data_base64,
94
- checksum: checksum,
93
+ data_base64:,
94
+ checksum:,
95
95
  recipient_sessions: session_ids.map(&->(s) { { session_id: s } }),
96
96
  silent: false
97
- }
97
+ }.compact
98
98
  end
99
99
 
100
100
  def send_encrypted_messages(messages)
@@ -104,19 +104,19 @@ module MixinBot
104
104
  # http post request
105
105
  def send_encrypted_message(payload)
106
106
  path = '/encrypted_messages'
107
- payload = [payload] unless payload.is_a?(Array)
108
- access_token ||= access_token('POST', path, payload.to_json)
109
- authorization = format('Bearer %<access_token>s', access_token: access_token)
110
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
107
+ payload = [payload] if payload.is_a? Hash
108
+ raise ArgumentError, 'Wrong payload format!' unless payload.is_a? Array
109
+
110
+ client.post path, *payload
111
111
  end
112
112
 
113
113
  def encrypt_message(data, sessions = [], sk: nil, pk: nil)
114
114
  raise ArgumentError, 'Wrong sessions format!' unless sessions.all?(&->(s) { s.key?('session_id') && s.key?('public_key') })
115
115
 
116
- sk = private_key[0...32]
117
- pk ||= private_key[32...]
116
+ sk ||= config.session_private_key[0...32]
117
+ pk ||= config.session_private_key[32...]
118
118
 
119
- checksum = Digest::MD5.hexdigest sessions.map(&->(s) { s['session_id'] }).sort.join
119
+ Digest::MD5.hexdigest sessions.map(&->(s) { s['session_id'] }).sort.join
120
120
  encrypter = OpenSSL::Cipher.new('AES-128-GCM').encrypt
121
121
  key = encrypter.random_key
122
122
  nounce = encrypter.random_iv
@@ -128,14 +128,14 @@ module MixinBot
128
128
  bytes = [1]
129
129
  bytes += [sessions.size].pack('v*').bytes
130
130
  bytes += JOSE::JWA::Ed25519.pk_to_curve25519(pk).bytes
131
-
131
+
132
132
  sessions.each do |session|
133
133
  aes_key = JOSE::JWA::X25519.shared_secret(
134
134
  Base64.urlsafe_decode64(session['public_key']),
135
135
  JOSE::JWA::Ed25519.secret_to_curve25519(sk)
136
136
  )
137
137
 
138
- padding = 16 - key.size % 16
138
+ padding = 16 - (key.size % 16)
139
139
  padtext = ([padding] * padding).pack('C*')
140
140
 
141
141
  encrypter = OpenSSL::Cipher.new('AES-256-CBC').encrypt
@@ -143,7 +143,7 @@ module MixinBot
143
143
  iv = encrypter.random_iv
144
144
  encrypter.iv = iv
145
145
 
146
- bytes += (MixinBot::Utils::UUID.new(hex: session['session_id']).packed + iv).bytes
146
+ bytes += (MixinBot::UUID.new(hex: session['session_id']).packed + iv).bytes
147
147
  bytes += encrypter.update(key + padtext).bytes
148
148
  end
149
149
 
@@ -156,19 +156,19 @@ module MixinBot
156
156
  def decrypt_message(data, sk: nil, si: nil)
157
157
  bytes = Base64.urlsafe_decode64(data).bytes
158
158
 
159
- si ||= session_id
160
- sk ||= private_key[0...32]
159
+ si ||= config.session_id
160
+ sk ||= config.session_private_key[0...32]
161
161
 
162
162
  size = 16 + 48
163
163
  return '' if bytes.size < 1 + 2 + 32 + size + 12
164
164
 
165
165
  session_length = bytes[1...3].pack('v*').unpack1('C*')
166
- prefix_size = 35 + session_length * size
166
+ prefix_size = 35 + (session_length * size)
167
167
 
168
168
  i = 35
169
169
  key = ''
170
170
  while i < prefix_size
171
- uuid = MixinBot::Utils::UUID.new(raw: bytes[i...(i + 16)].pack('C*')).unpacked
171
+ uuid = MixinBot::UUID.new(raw: bytes[i...(i + 16)].pack('C*')).unpacked
172
172
  if uuid == si
173
173
  pub = bytes[3...35]
174
174
  aes_key = JOSE::JWA::X25519.shared_secret(
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyMultisig
6
+ MULTISIG_REQUEST_ACTIONS = %i[sign unlock].freeze
7
+ def create_multisig_request(action, raw, access_token: nil)
8
+ raise ArgumentError, "request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}" unless MULTISIG_REQUEST_ACTIONS.include? action.to_sym
9
+
10
+ path = '/multisigs/requests'
11
+ payload = {
12
+ action:,
13
+ raw:
14
+ }
15
+ client.post path, **payload, access_token:
16
+ end
17
+
18
+ # transfer from the multisig address
19
+ def create_sign_multisig_request(raw, access_token: nil)
20
+ create_multisig_request 'sign', raw, access_token:
21
+ end
22
+
23
+ # transfer from the multisig address
24
+ # create a request for unlock a multi-sign
25
+ def create_unlock_multisig_request(raw, access_token: nil)
26
+ create_multisig_request 'unlock', raw, access_token:
27
+ end
28
+
29
+ def sign_multisig_request(request_id, pin = nil)
30
+ pin ||= config.pin
31
+ path = format('/multisigs/requests/%<request_id>s/sign', request_id:)
32
+ payload =
33
+ if pin.length > 6
34
+ {
35
+ pin_base64: encrypt_tip_pin(pin, 'TIP:MULTISIG:REQUEST:SIGN:', request_id)
36
+ }
37
+ else
38
+ {
39
+ pin: encrypt_pin(pin)
40
+ }
41
+ end
42
+
43
+ client.post path, **payload, access_token:
44
+ end
45
+
46
+ def unlock_multisig_request(request_id, pin = nil)
47
+ pin ||= config.pin
48
+
49
+ path = format('/multisigs/requests/%<request_id>s/unlock', request_id:)
50
+ payload =
51
+ if pin.length > 6
52
+ {
53
+ pin_base64: encrypt_tip_pin(pin, 'TIP:MULTISIG:REQUEST:UNLOCK:', request_id)
54
+ }
55
+ else
56
+ {
57
+ pin: encrypt_pin(pin)
58
+ }
59
+ end
60
+
61
+ client.post path, **payload
62
+ end
63
+
64
+ # pay to the multisig address
65
+ # used for create multisig payment code_id
66
+ def create_multisig_payment(**kwargs)
67
+ path = '/payments'
68
+ payload = {
69
+ asset_id: kwargs[:asset_id],
70
+ amount: format('%.8f', kwargs[:amount].to_d),
71
+ trace_id: kwargs[:trace_id] || SecureRandom.uuid,
72
+ memo: kwargs[:memo],
73
+ opponent_multisig: {
74
+ receivers: kwargs[:receivers],
75
+ threshold: kwargs[:threshold]
76
+ }
77
+ }
78
+ client.post path, **payload, access_token: kwargs[:access_token]
79
+ end
80
+
81
+ def verify_multisig(code_id, access_token: nil)
82
+ path = format('/codes/%<code_id>s', code_id:)
83
+ client.get path, access_token:
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyOutput
6
+ def outputs(**kwargs)
7
+ limit = kwargs[:limit] || 100
8
+ offset = kwargs[:offset] || ''
9
+ state = kwargs[:state] || ''
10
+ members = kwargs[:members] || []
11
+ threshold = kwargs[:threshold] || ''
12
+ access_token = kwargs[:access_token]
13
+ members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
14
+
15
+ path = '/multisigs/outputs'
16
+ params = {
17
+ limit:,
18
+ offset:,
19
+ state:,
20
+ members:,
21
+ threshold:
22
+ }.compact_blank
23
+
24
+ client.get path, **params, access_token:
25
+ end
26
+ alias multisigs outputs
27
+ alias multisig_outputs outputs
28
+
29
+ def create_output(receivers:, index:, hint: nil, access_token: nil)
30
+ path = '/outputs'
31
+ payload = {
32
+ receivers:,
33
+ index:,
34
+ hint:
35
+ }
36
+ client.post path, **payload, access_token:
37
+ end
38
+
39
+ def build_output(receivers:, index:, amount:, threshold:, hint: nil)
40
+ _output = create_output(receivers:, index:, hint:)
41
+ {
42
+ amount: format('%.8f', amount.to_d.to_r),
43
+ script: build_threshold_script(threshold),
44
+ mask: _output['mask'],
45
+ keys: _output['keys']
46
+ }
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyPayment
6
+ def pay_url(**kwargs)
7
+ format(
8
+ 'https://mixin.one/pay?recipient=%<recipient_id>s&asset=%<asset>s&amount=%<amount>s&trace=%<trace>s&memo=%<memo>s',
9
+ recipient_id: kwargs[:recipient_id],
10
+ asset: kwargs[:asset_id],
11
+ amount: kwargs[:amount].to_s,
12
+ trace: kwargs[:trace],
13
+ memo: kwargs[:memo]
14
+ )
15
+ end
16
+
17
+ # https://developers.mixin.one/api/alpha-mixin-network/verify-payment/
18
+ def verify_payment(**kwargs)
19
+ path = '/payments'
20
+ payload = {
21
+ asset_id: kwargs[:asset_id],
22
+ opponent_id: kwargs[:opponent_id],
23
+ amount: kwargs[:amount].to_s,
24
+ trace_id: kwargs[:trace]
25
+ }
26
+
27
+ client.post path, **payload
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacySnapshot
6
+ def network_snapshots(**kwargs)
7
+ path = '/network/snapshots'
8
+ params = {
9
+ limit: kwargs[:limit],
10
+ offset: kwargs[:offset],
11
+ asset: kwargs[:asset],
12
+ order: kwargs[:order]
13
+ }
14
+
15
+ client.get path, **params, access_token: kwargs[:access_token]
16
+ end
17
+
18
+ def snapshots(**kwargs)
19
+ path = '/snapshots'
20
+
21
+ params = {
22
+ limit: kwargs[:limit],
23
+ offset: kwargs[:offset],
24
+ asset: kwargs[:asset],
25
+ opponent: kwargs[:opponent],
26
+ order: kwargs[:order]
27
+ }
28
+
29
+ client.get path, **params, access_token: kwargs[:access_token]
30
+ end
31
+
32
+ def network_snapshot(snapshot_id, **kwargs)
33
+ path = format('/network/snapshots/%<snapshot_id>s', snapshot_id:)
34
+
35
+ client.get path, access_token: kwargs[:access_token]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyTransaction
6
+ LEGACY_TX_VERSION = 0x04
7
+
8
+ # use safe transaction protocol instead
9
+ # kwargs:
10
+ # {
11
+ # senders: [ uuid ],
12
+ # senders_threshold: integer,
13
+ # receivers: [ uuid ],
14
+ # receivers_threshold: integer,
15
+ # asset_id: uuid,
16
+ # amount: string / float,
17
+ # memo: string,
18
+ # }
19
+ RAW_TRANSACTION_ARGUMENTS = %i[utxos senders senders_threshold receivers receivers_threshold amount].freeze
20
+ def build_raw_transaction(**kwargs)
21
+ raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
22
+
23
+ senders = kwargs[:senders]
24
+ senders_threshold = kwargs[:senders_threshold]
25
+ receivers = kwargs[:receivers]
26
+ receivers_threshold = kwargs[:receivers_threshold]
27
+ amount = kwargs[:amount]
28
+ asset_id = kwargs[:asset_id]
29
+ asset_mixin_id = kwargs[:asset_mixin_id]
30
+ utxos = kwargs[:utxos]
31
+ extra = kwargs[:extra]
32
+ access_token = kwargs[:access_token]
33
+ outputs = kwargs[:outputs] || []
34
+ hint = kwargs[:hint]
35
+ version = kwargs[:version] || LEGACY_TX_VERSION
36
+
37
+ raise 'access_token required!' if access_token.nil? && !senders.include?(config.app_id)
38
+
39
+ amount = amount.to_d.round(8)
40
+ input_amount = utxos.map(
41
+ &lambda { |utxo|
42
+ utxo['amount'].to_d
43
+ }
44
+ ).sum
45
+
46
+ if input_amount < amount
47
+ raise format(
48
+ 'not enough amount! %<input_amount>s < %<amount>s',
49
+ input_amount:,
50
+ amount:
51
+ )
52
+ end
53
+
54
+ inputs = utxos.map(
55
+ &lambda { |utx|
56
+ {
57
+ 'hash' => utx['transaction_hash'],
58
+ 'index' => utx['output_index']
59
+ }
60
+ }
61
+ )
62
+
63
+ if outputs.empty?
64
+ receivers_threshold = 1 if receivers.size == 1
65
+ output0 = build_output(
66
+ receivers:,
67
+ index: 0,
68
+ amount:,
69
+ threshold: receivers_threshold,
70
+ hint:
71
+ )
72
+ outputs.push output0
73
+
74
+ if input_amount > amount
75
+ output1 = build_output(
76
+ receivers: senders,
77
+ index: 1,
78
+ amount: input_amount - amount,
79
+ threshold: senders_threshold,
80
+ hint:
81
+ )
82
+ outputs.push output1
83
+ end
84
+ end
85
+
86
+ asset = asset_mixin_id || SHA3::Digest::SHA256.hexdigest(asset_id)
87
+ {
88
+ version:,
89
+ asset:,
90
+ inputs:,
91
+ outputs:,
92
+ extra:
93
+ }
94
+ end
95
+
96
+ # use safe transaction protocol instead
97
+ MULTISIG_TRANSACTION_ARGUMENTS = %i[asset_id receivers threshold amount].freeze
98
+ def create_multisig_transaction(pin, **options)
99
+ raise ArgumentError, "#{MULTISIG_TRANSACTION_ARGUMENTS.join(', ')} are needed for create multisig transaction" unless MULTISIG_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
100
+
101
+ asset_id = options[:asset_id]
102
+ receivers = options[:receivers].sort
103
+ threshold = options[:threshold]
104
+ amount = format('%.8f', options[:amount].to_d.to_r)
105
+ memo = options[:memo]
106
+ trace_id = options[:trace_id] || SecureRandom.uuid
107
+
108
+ path = '/transactions'
109
+ payload = {
110
+ asset_id:,
111
+ opponent_multisig: {
112
+ receivers:,
113
+ threshold:
114
+ },
115
+ amount:,
116
+ trace_id:,
117
+ memo:
118
+ }
119
+
120
+ if pin.length > 6
121
+ payload[:pin_base64] = encrypt_tip_pin(pin, 'TIP:TRANSACTION:CREATE:', asset_id, receivers.join, threshold, amount, trace_id, memo)
122
+ else
123
+ payload[:pin] = encrypt_pin(pin)
124
+ end
125
+
126
+ client.post path, **payload
127
+ end
128
+
129
+ # use safe transaction protocol instead
130
+ MAINNET_TRANSACTION_ARGUMENTS = %i[asset_id opponent_id amount].freeze
131
+ def create_mainnet_transaction(pin, **options)
132
+ raise ArgumentError, "#{MAINNET_TRANSACTION_ARGUMENTS.join(', ')} are needed for create main net transactions" unless MAINNET_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
133
+
134
+ asset_id = options[:asset_id]
135
+ opponent_id = options[:opponent_id]
136
+ amount = format('%.8f', options[:amount].to_d)
137
+ memo = options[:memo]
138
+ trace_id = options[:trace_id] || SecureRandom.uuid
139
+
140
+ path = '/transactions'
141
+ payload = {
142
+ asset_id:,
143
+ opponent_id:,
144
+ amount:,
145
+ trace_id:,
146
+ memo:
147
+ }
148
+
149
+ if pin.length > 6
150
+ payload[:pin_base64] = encrypt_tip_pin(pin, 'TIP:TRANSACTION:CREATE:', asset_id, opponent_id, amount, trace_id, memo)
151
+ else
152
+ payload[:pin] = encrypt_pin(pin)
153
+ end
154
+
155
+ client.post path, **payload
156
+ end
157
+
158
+ # use safe transaction protocol instead
159
+ def transactions(**options)
160
+ path = '/external/transactions'
161
+ params = {
162
+ limit: options[:limit],
163
+ offset: options[:offset],
164
+ asset: options[:asset],
165
+ destination: options[:destination],
166
+ tag: options[:tag]
167
+ }
168
+
169
+ client.get path, **params
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MixinBot
4
+ class API
5
+ module LegacyTransfer
6
+ TRANSFER_ARGUMENTS = %i[asset_id opponent_id amount].freeze
7
+ def create_transfer(pin, **kwargs)
8
+ raise ArgumentError, "#{TRANSFER_ARGUMENTS.join(', ')} are needed for create transfer" unless TRANSFER_ARGUMENTS.all? { |param| kwargs.keys.include? param }
9
+
10
+ asset_id = kwargs[:asset_id]
11
+ opponent_id = kwargs[:opponent_id]
12
+ amount = format('%.8f', kwargs[:amount].to_d.to_r).gsub(/\.?0+$/, '')
13
+ trace_id = kwargs[:trace_id] || SecureRandom.uuid
14
+ memo = kwargs[:memo] || ''
15
+
16
+ payload = {
17
+ asset_id:,
18
+ opponent_id:,
19
+ amount:,
20
+ trace_id:,
21
+ memo:
22
+ }
23
+
24
+ if pin.length > 6
25
+ pin_base64 = encrypt_tip_pin pin, 'TIP:TRANSFER:CREATE:', asset_id, opponent_id, amount, trace_id, memo
26
+ payload[:pin_base64] = pin_base64
27
+ else
28
+ encrypted_pin = encrypt_pin pin
29
+ payload[:pin] = encrypted_pin
30
+ end
31
+
32
+ path = '/transfers'
33
+ client.post path, **payload
34
+ end
35
+
36
+ def transfer(trace_id, access_token: nil)
37
+ path = format('/transfers/trace/%<trace_id>s', trace_id:)
38
+ client.get path, access_token:
39
+ end
40
+ end
41
+ end
42
+ end
@@ -3,37 +3,33 @@
3
3
  module MixinBot
4
4
  class API
5
5
  module Me
6
- # https://developers.mixin.one/api/beta-mixin-message/read-profile/
7
6
  def me(access_token: nil)
8
7
  path = '/me'
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 })
8
+ client.get path, access_token:
12
9
  end
13
- alias read_me me
14
10
 
15
- # https://developers.mixin.one/api/beta-mixin-message/update-profile/
16
11
  # avatar_base64:
17
- # String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
18
- def update_me(full_name:, avatar_base64: nil, access_token: nil)
12
+ # String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
13
+ def update_me(**kwargs)
19
14
  path = '/me'
20
15
  payload = {
21
- full_name: full_name,
22
- avatar_base64: avatar_base64
16
+ full_name: kwargs[:full_name],
17
+ avatar_base64: kwargs[:avatar_base64],
18
+ access_token: kwargs[:access_token]
23
19
  }
24
- access_token ||= access_token('POST', path, payload.to_json)
25
- authorization = format('Bearer %<access_token>s', access_token: access_token)
26
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
20
+ client.post path, **payload
27
21
  end
28
22
 
29
23
  # https://developers.mixin.one/api/beta-mixin-message/friends/
30
24
  def friends(access_token: nil)
31
25
  path = '/friends'
32
- access_token ||= access_token('GET', path, '')
33
- authorization = format('Bearer %<access_token>s', access_token: access_token)
34
- client.get(path, headers: { 'Authorization': authorization })
26
+ client.get path, access_token:
27
+ end
28
+
29
+ def safe_me(access_token: nil)
30
+ path = '/safe/me'
31
+ client.get path, access_token:
35
32
  end
36
- alias read_friends friends
37
33
  end
38
34
  end
39
35
  end
@@ -10,10 +10,10 @@ module MixinBot
10
10
 
11
11
  def acknowledge_message_receipt(message_id)
12
12
  params = {
13
- message_id: message_id,
13
+ message_id:,
14
14
  status: 'READ'
15
15
  }
16
- write_ws_message(action: 'ACKNOWLEDGE_MESSAGE_RECEIPT', params: params)
16
+ write_ws_message(action: 'ACKNOWLEDGE_MESSAGE_RECEIPT', params:)
17
17
  end
18
18
 
19
19
  def plain_text(options)
@@ -72,7 +72,7 @@ module MixinBot
72
72
  options.merge!(
73
73
  category: 'MESSAGE_RECALL',
74
74
  data: {
75
- message_id: message_id
75
+ message_id:
76
76
  }
77
77
  )
78
78
  base_message_params(options)
@@ -90,11 +90,11 @@ module MixinBot
90
90
  quote_message_id: options[:quote_message_id],
91
91
  message_id: options[:message_id] || SecureRandom.uuid,
92
92
  data: Base64.encode64(data)
93
- }
93
+ }.compact
94
94
  end
95
95
 
96
96
  # read the gzipped message form websocket
97
- def read_ws_message(data)
97
+ def ws_message(data)
98
98
  io = StringIO.new(data.pack('c*'), 'rb')
99
99
  gzip = Zlib::GzipReader.new io
100
100
  msg = gzip.read
@@ -107,8 +107,8 @@ module MixinBot
107
107
  def write_ws_message(params:, action: 'CREATE_MESSAGE')
108
108
  msg = {
109
109
  id: SecureRandom.uuid,
110
- action: action,
111
- params: params
110
+ action:,
111
+ params:
112
112
  }.to_json
113
113
 
114
114
  io = StringIO.new 'wb'
@@ -158,9 +158,12 @@ module MixinBot
158
158
  # http post request
159
159
  def send_message(payload)
160
160
  path = '/messages'
161
- access_token ||= access_token('POST', path, payload.to_json)
162
- authorization = format('Bearer %<access_token>s', access_token: access_token)
163
- client.post(path, headers: { 'Authorization': authorization }, json: payload)
161
+
162
+ if payload.is_a? Hash
163
+ client.post path, **payload
164
+ elsif payload.is_a? Array
165
+ client.post path, *payload
166
+ end
164
167
  end
165
168
  end
166
169
  end