mixin_bot 0.12.1 → 1.0.0

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.
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