mixin_bot 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1bfa2114ffe972196a7d934cc499f08cc43b108a274ebca7740cf7ce150b58c
4
- data.tar.gz: 7fd0378e46a47bdb0ce558496dff98327f66f0b9cfef16e5cb042cfd3c511f32
3
+ metadata.gz: 7cc49837c37f9c79f664694d45f0ba3e6299fa06f38ef4fe51531eb0798f97e5
4
+ data.tar.gz: 183932450eac9eff899aa29c2a99278890b2a2fc6f09a36f4d2f602526022dfc
5
5
  SHA512:
6
- metadata.gz: 46d7366d604e930323471ab60cbec49fe8e881dc5a3d58b5a9302712ea17c611d8cd0c7a99b6afde424ab028d28a810a52cb7a790bf945aa8327765e4fb653e5
7
- data.tar.gz: fef02f6c7722bb033317be3ddc9878b4a0739f88b676192fbc85b956a0cb51aaf477a25737e925b1388da3402a48989d691a75fe4dc16877f8b9c6416c8be4c3
6
+ metadata.gz: 2ace6e9270492ee1d3c18154408bfd76a09c35bf4bd4cffc434b070d89a65957b2f38553f62f61854aa8976907546ddec504a4281104fd9b3cb6034e55846911
7
+ data.tar.gz: d41892aa1a7014c77b32cda0b27db3a967f97b7547c90e222b55eefb3229d175fefd98738e31e0560e7c78e558d47cee0256cc9caddd87cdf9233284a5e0a47b
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: false
2
+
3
+ module MixinBot
4
+ class API
5
+ module EncryptedMessage
6
+ def encrypted_text(options)
7
+ options.merge!(category: 'ENCRYPTED_TEXT')
8
+ base_encrypted_message_params(options)
9
+ end
10
+
11
+ def encrypted_post(options)
12
+ options.merge!(category: 'ENCRYPTED_POST')
13
+ base_encrypted_message_params(options)
14
+ end
15
+
16
+ def encrypted_image(options)
17
+ options.merge!(category: 'ENCRYPTED_IMAGE')
18
+ base_encrypted_message_params(options)
19
+ end
20
+
21
+ def encrypted_data(options)
22
+ options.merge!(category: 'ENCRYPTED_DATA')
23
+ base_encrypted_message_params(options)
24
+ end
25
+
26
+ def encrypted_sticker(options)
27
+ options.merge!(category: 'ENCRYPTED_STICKER')
28
+ base_encrypted_message_params(options)
29
+ end
30
+
31
+ def encrypted_contact(options)
32
+ options.merge!(category: 'ENCRYPTED_CONTACT')
33
+ base_encrypted_message_params(options)
34
+ end
35
+
36
+ def encrypted_audio(options)
37
+ options.merge!(category: 'ENCRYPTED_AUDIO')
38
+ base_encrypted_message_params(options)
39
+ end
40
+
41
+ def encrypted_video(options)
42
+ options.merge!(category: 'ENCRYPTED_VIDEO')
43
+ base_encrypted_message_params(options)
44
+ end
45
+
46
+ # use HTTP to send message
47
+ def send_encrypted_text_message(options)
48
+ send_encrypted_message encrypted_text(options)
49
+ end
50
+
51
+ def send_encrypted_post_message(options)
52
+ send_encrypted_message encrypted_post(options)
53
+ end
54
+
55
+ def send_encrypted_image_message(options)
56
+ send_encrypted_message encrypted_image(options)
57
+ end
58
+
59
+ def send_encrypted_data_message(options)
60
+ send_encrypted_message encrypted_data(options)
61
+ end
62
+
63
+ def send_encrypted_sticker_message(options)
64
+ send_encrypted_message encrypted_sticker(options)
65
+ end
66
+
67
+ def send_encrypted_contact_message(options)
68
+ send_encrypted_message encrypted_contact(options)
69
+ end
70
+
71
+ def send_encrypted_audio_message(options)
72
+ send_encrypted_message encrypted_audio(options)
73
+ end
74
+
75
+ def send_encrypted_video_message(options)
76
+ send_encrypted_message encrypted_video(options)
77
+ end
78
+
79
+ # base format of message params
80
+ def base_encrypted_message_params(options)
81
+ data = options[:data].is_a?(String) ? options[:data] : options[:data].to_json
82
+ data_base64 = encrypt_message Base64.urlsafe_encode64(data, padding: false), options[:sessions]
83
+ session_ids = options[:sessions].map(&->(s) { s['session_id'] }).sort
84
+ checksum = Digest::MD5.hexdigest session_ids.join
85
+
86
+ {
87
+ conversation_id: options[:conversation_id],
88
+ recipient_id: options[:recipient_id],
89
+ representative_id: options[:representative_id],
90
+ category: options[:category],
91
+ quote_message_id: options[:quote_message_id],
92
+ message_id: options[:message_id] || SecureRandom.uuid,
93
+ data_base64: data_base64,
94
+ checksum: checksum,
95
+ recipient_sessions: session_ids.map(&->(s) { { session_id: s } }),
96
+ silent: false
97
+ }
98
+ end
99
+
100
+ def send_encrypted_messages(messages)
101
+ send_encrypted_message messages
102
+ end
103
+
104
+ # http post request
105
+ def send_encrypted_message(payload)
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)
111
+ end
112
+
113
+ def encrypt_message(data, sessions = [], sk: nil, pk: nil)
114
+ raise ArgumentError, 'Wrong sessions format!' unless sessions.all?(&->(s) { s.key?('session_id') && s.key?('public_key') })
115
+
116
+ sk = private_key[0...32]
117
+ pk ||= private_key[32...]
118
+
119
+ checksum = Digest::MD5.hexdigest sessions.map(&->(s) { s['session_id'] }).sort.join
120
+ encrypter = OpenSSL::Cipher.new('AES-128-GCM').encrypt
121
+ key = encrypter.random_key
122
+ nounce = encrypter.random_iv
123
+ encrypter.key = key
124
+ encrypter.iv = nounce
125
+ encrypter.auth_data = ''
126
+ ciphertext = encrypter.update(Base64.urlsafe_decode64(data)) + encrypter.final + encrypter.auth_tag
127
+
128
+ bytes = [1]
129
+ bytes += [sessions.size].pack('v*').bytes
130
+ bytes += JOSE::JWA::Ed25519.pk_to_curve25519(pk).bytes
131
+
132
+ sessions.each do |session|
133
+ aes_key = JOSE::JWA::X25519.shared_secret(
134
+ Base64.urlsafe_decode64(session['public_key']),
135
+ JOSE::JWA::Ed25519.secret_to_curve25519(sk)
136
+ )
137
+
138
+ padding = 16 - key.size % 16
139
+ padtext = ([padding] * padding).pack('C*')
140
+
141
+ encrypter = OpenSSL::Cipher.new('AES-256-CBC').encrypt
142
+ encrypter.key = aes_key
143
+ iv = encrypter.random_iv
144
+ encrypter.iv = iv
145
+
146
+ bytes += (MixinBot::Utils::UUID.new(hex: session['session_id']).packed + iv).bytes
147
+ bytes += encrypter.update(key + padtext).bytes
148
+ end
149
+
150
+ bytes += nounce.bytes
151
+ bytes += ciphertext.bytes
152
+
153
+ Base64.urlsafe_encode64 bytes.pack('C*'), padding: false
154
+ end
155
+
156
+ def decrypt_message(data, sk: nil, si: nil)
157
+ bytes = Base64.urlsafe_decode64(data).bytes
158
+
159
+ si ||= session_id
160
+ sk ||= private_key[0...32]
161
+
162
+ size = 16 + 48
163
+ return '' if bytes.size < 1 + 2 + 32 + size + 12
164
+
165
+ session_length = bytes[1...3].pack('v*').unpack1('C*')
166
+ prefix_size = 35 + session_length * size
167
+
168
+ i = 35
169
+ key = ''
170
+ while i < prefix_size
171
+ uuid = MixinBot::Utils::UUID.new(raw: bytes[i...(i + 16)].pack('C*')).unpacked
172
+ if uuid == si
173
+ pub = bytes[3...35]
174
+ aes_key = JOSE::JWA::X25519.shared_secret(
175
+ pub.pack('C*'),
176
+ JOSE::JWA::Ed25519.secret_to_curve25519(sk)
177
+ )
178
+ iv = bytes[(i + 16)...(i + 16 + 16)].pack('C*')
179
+ encrypted_key = bytes[(i + 16 + 16)...(i + size)].pack('C*')
180
+
181
+ decrypter = OpenSSL::Cipher.new('AES-256-CBC').decrypt
182
+ decrypter.iv = iv
183
+ decrypter.key = aes_key
184
+ cipher = decrypter.update(encrypted_key)
185
+ key = cipher[...16]
186
+ break
187
+ end
188
+ i += size
189
+ end
190
+
191
+ return '' unless key.size == 16
192
+
193
+ decrypter = OpenSSL::Cipher.new('AES-128-GCM').decrypt
194
+ decrypter.key = key
195
+ decrypter.iv = bytes[prefix_size...(prefix_size + 12)].pack('C*')
196
+ decrypter.auth_tag = bytes.last(16).pack('C*')
197
+ decrypted = decrypter.update(bytes[(prefix_size + 12)...(bytes.size - 16)].pack('C*'))
198
+ decrypter.final
199
+
200
+ Base64.urlsafe_encode64 decrypted
201
+ end
202
+ end
203
+ end
204
+ end
@@ -8,15 +8,6 @@ module MixinBot
8
8
  write_ws_message(action: 'LIST_PENDING_MESSAGES', params: {})
9
9
  end
10
10
 
11
- # ACKNOWLEDGE_MESSAGE_RECEIPT ack server received message
12
- # {
13
- # "id": "UUID",
14
- # "action": "ACKNOWLEDGE_MESSAGE_RECEIPT",
15
- # "params": {
16
- # "message_id": "UUID // message_id is you received message's message_id",
17
- # "status": "READ"
18
- # }
19
- # }
20
11
  def acknowledge_message_receipt(message_id)
21
12
  params = {
22
13
  message_id: message_id,
@@ -25,200 +16,56 @@ module MixinBot
25
16
  write_ws_message(action: 'ACKNOWLEDGE_MESSAGE_RECEIPT', params: params)
26
17
  end
27
18
 
28
- # {
29
- # "id": "UUID // generated by client",
30
- # "action": "CREATE_MESSAGE",
31
- # "params": {
32
- # "conversation_id": "UUID",
33
- # "category": "PLAIN_TEXT",
34
- # "status": "SENT",
35
- # "message_id": "UUID // generated by client",
36
- # "data": "Base64 encoded data" ,
37
- # }
38
- # }
39
19
  def plain_text(options)
40
20
  options.merge!(category: 'PLAIN_TEXT')
41
21
  base_message_params(options)
42
22
  end
43
23
 
44
- # {
45
- # "id": "UUID // generated by client",
46
- # "action": "CREATE_MESSAGE",
47
- # "params": {
48
- # "conversation_id": "UUID",
49
- # "category": "PLAIN_POST",
50
- # "status": "SENT",
51
- # "message_id": "UUID // generated by client",
52
- # "data": "Base64 encoded data content is markdown" ,
53
- # }
54
- # }
55
24
  def plain_post(options)
56
25
  options.merge!(category: 'PLAIN_POST')
57
26
  base_message_params(options)
58
27
  end
59
28
 
60
- # {
61
- # "id": "UUID",
62
- # "action": "CREATE_MESSAGE",
63
- # "params": {
64
- # "conversation_id": "UUID"
65
- # "category": "PLAIN_IMAGE"
66
- # "status": "SENT",
67
- # "message_id": "UUID",
68
- # "data": "Base64 encoded data"
69
- # }
70
- # }
71
- # data format:
72
- # {
73
- # "attachment_id":
74
- # "Read From POST /attachments",
75
- # "mime_type": "",
76
- # "width": 1024,
77
- # "height": 1024,
78
- # "size": 1024,
79
- # "thumbnail": "base64 encoded"
80
- # }
81
29
  def plain_image(options)
82
30
  options.merge!(category: 'PLAIN_IMAGE')
83
31
  base_message_params(options)
84
32
  end
85
33
 
86
- # {
87
- # "id": "UUID",
88
- # "action": "CREATE_MESSAGE",
89
- # "params": {
90
- # "conversation_id": "UUID",
91
- # "category": "PLAIN_DATA",
92
- # "status": "SENT",
93
- # "message_id": "UUID",
94
- # "data": "Base64 encoded data",
95
- # }
96
- # }
97
- # data format:
98
- # {
99
- # "attachment_id": "Read From POST /attachments",
100
- # "mime_type": "",
101
- # "size": 1024,
102
- # "name": "Share"
103
- # }
104
34
  def plain_data(options)
105
35
  options.merge!(category: 'PLAIN_DATA')
106
36
  base_message_params(options)
107
37
  end
108
38
 
109
- # {
110
- # "id": "UUID",
111
- # "action": "CREATE_MESSAGE",
112
- # "params": {
113
- # "conversation_id": "UUID",
114
- # "category": "PLAIN_STICKER",
115
- # "status": "SENT",
116
- # "message_id": "UUID",
117
- # "data": "Base64 encoded data"
118
- # }
119
- # }
120
- # data format:
121
- # {
122
- # "name": "hello",
123
- # "album_id": "UUID"
124
- # }
125
39
  def plain_sticker(options)
126
40
  options.merge!(category: 'PLAIN_STICKER')
127
41
  base_message_params(options)
128
42
  end
129
43
 
130
- # {
131
- # "id": "UUID",
132
- # "action": "CREATE_MESSAGE",
133
- # "params": {
134
- # "conversation_id": "UUID",
135
- # "category": "PLAIN_CONTACT"
136
- # "status": "SENT",
137
- # "message_id": "UUID",
138
- # "data": "Base64 encoded data"
139
- # }
140
- # }
141
- # data format:
142
- # { "user_id": "UUID"}
143
44
  def plain_contact(options)
144
45
  options.merge!(category: 'PLAIN_CONTACT')
145
46
  base_message_params(options)
146
47
  end
147
48
 
148
- # {
149
- # "id": "UUID",
150
- # "action": "CREATE_MESSAGE",
151
- # "params": {
152
- # "conversation_id": "UUID",
153
- # "category": "APP_CARD",
154
- # "status": "SENT",
155
- # "message_id": "UUID",
156
- # "data": "Base64 encoded data"
157
- # }
158
- # }
159
- # data format:
160
- # {
161
- # "icon_url": "https://mixin.one/assets/98b586edb270556d1972112bd7985e9e.png",
162
- # "title": "Mixin",
163
- # "description": "A free and lightning fast peer-to-peer transactional network for digital assets.",
164
- # "action": "https://mixin.one"
165
- # }
49
+ def plain_audio(options)
50
+ options.merge!(category: 'PLAIN_AUDIO')
51
+ base_message_params(options)
52
+ end
53
+
54
+ def plain_video(options)
55
+ options.merge!(category: 'PLAIN_VIDEO')
56
+ base_message_params(options)
57
+ end
58
+
166
59
  def app_card(options)
167
60
  options.merge!(category: 'APP_CARD')
168
61
  base_message_params(options)
169
62
  end
170
63
 
171
- # {
172
- # "id": "UUID",
173
- # "action": "CREATE_MESSAGE",
174
- # "params": {
175
- # "conversation_id": "UUID",
176
- # "category": "APP_BUTTON_GROUP",
177
- # "status": "SENT",
178
- # "message_id": "UUID",
179
- # "data": "Base64 encoded data"
180
- # }
181
- # }
182
- # data format:
183
- # [
184
- # {
185
- # "label": "Mixin Website",
186
- # "color": "#ABABAB",
187
- # "action": "https://mixin.one"
188
- # },
189
- # ...
190
- # ]
191
64
  def app_button_group(options)
192
65
  options.merge!(category: 'APP_BUTTON_GROUP')
193
66
  base_message_params(options)
194
67
  end
195
68
 
196
- # {
197
- # "id": "UUID",
198
- # "action": "CREATE_MESSAGE",
199
- # "params": {
200
- # "conversation_id": "UUID",
201
- # "category": "PLAIN_VIDEO",
202
- # "status": "SENT",
203
- # "message_id": "UUID",
204
- # "data": "Base64 encoded data"
205
- # }
206
- # }
207
- # data format:
208
- # {
209
- # "attachment_id": "Read From POST /attachments",
210
- # "mime_type": "",
211
- # "width": 1024,
212
- # "height": 1024,
213
- # "size": 1024,
214
- # "duration": 1024,
215
- # "thumbnail": "base64 encoded"
216
- # }
217
- def plain_video(options)
218
- options.merge!(category: 'PLAIN_VIDEO')
219
- base_message_params(options)
220
- end
221
-
222
69
  def recall_message_params(message_id, options)
223
70
  raise 'recipient_id is required!' if options[:recipient_id].nil?
224
71
 
@@ -304,24 +151,6 @@ module MixinBot
304
151
  send_message [recall_message_params(message_id, options)]
305
152
  end
306
153
 
307
- # {
308
- # "id": "UUID",
309
- # "action": "CREATE_PLAIN_MESSAGES",
310
- # "params": {
311
- # "messages": [
312
- # {
313
- # "conversation_id": "UUID",
314
- # "recipient_id": "UUID",
315
- # "message_id": "UUID",
316
- # "representative_id": "UUID (optional, only supported in peer to peer conversation)",
317
- # "quote_message_id": "UUID (optional, only supported text, e.g. PLAIN_TEXT)",
318
- # "category": "Only support plain category e.g.: PLAIN_TEXT, PLAIN_STICKER etc",
319
- # "data": "Correspond to category."
320
- # },
321
- # ...
322
- # ]
323
- # }
324
- # }
325
154
  def send_plain_messages(messages)
326
155
  send_message messages
327
156
  end
@@ -25,7 +25,7 @@ module MixinBot
25
25
  offset: options[:offset],
26
26
  asset: options[:asset],
27
27
  opponent: options[:opponent],
28
- order: options[:order],
28
+ order: options[:order]
29
29
  )
30
30
 
31
31
  access_token = options[:access_token] || access_token('GET', path)
data/lib/mixin_bot/api.rb CHANGED
@@ -8,6 +8,7 @@ require_relative './api/auth'
8
8
  require_relative './api/blaze'
9
9
  require_relative './api/collectible'
10
10
  require_relative './api/conversation'
11
+ require_relative './api/encrypted_message'
11
12
  require_relative './api/me'
12
13
  require_relative './api/message'
13
14
  require_relative './api/multisig'
@@ -83,6 +84,7 @@ module MixinBot
83
84
  include MixinBot::API::Blaze
84
85
  include MixinBot::API::Collectible
85
86
  include MixinBot::API::Conversation
87
+ include MixinBot::API::EncryptedMessage
86
88
  include MixinBot::API::Me
87
89
  include MixinBot::API::Message
88
90
  include MixinBot::API::Multisig
@@ -6,12 +6,12 @@ module MixinBot
6
6
  option :keystore, type: :string, aliases: '-k', required: true, desc: 'keystore or keystore.json file path'
7
7
  option :iterator, type: :string, aliases: '-i', desc: 'Iterator'
8
8
  def encrypt(pin)
9
- log api_instance.encrypt_pin options[:pin].to_s, iterator: options[:iterator]
9
+ log api_instance.encrypt_pin pin.to_s, iterator: options[:iterator]
10
10
  end
11
11
 
12
12
  desc 'unique UUIDS', 'generate unique UUID for two or more UUIDs'
13
13
  def unique(*uuids)
14
- log MixinBot::Utils.unique_uuid *uuids
14
+ log MixinBot::Utils.unique_uuid(*uuids)
15
15
  end
16
16
 
17
17
  desc 'generatetrace HASH', 'generate trace ID from Tx hash'
@@ -31,7 +31,7 @@ module MixinBot
31
31
 
32
32
  @collection = NULL_UUID if collection.blank?
33
33
  @chain = NFT_MEMO_DEFAULT_CHAIN
34
- @nm_class= NFT_MEMO_DEFAULT_CLASS
34
+ @nm_class = NFT_MEMO_DEFAULT_CLASS
35
35
  mark 0
36
36
  encode
37
37
 
@@ -59,9 +59,8 @@ module MixinBot
59
59
 
60
60
  def mark(*indexes)
61
61
  indexes.map do |index|
62
- if index >= 64 || index < 0
63
- raise ArgumentError, "invalid NFO memo index #{index}"
64
- end
62
+ raise ArgumentError, "invalid NFO memo index #{index}" if index >= 64 || index.negative?
63
+
65
64
  @mask = mask ^ (1 << index)
66
65
  end
67
66
  end
@@ -69,7 +68,7 @@ module MixinBot
69
68
  def encode
70
69
  bytes = []
71
70
 
72
- bytes += prefix.bytes
71
+ bytes += prefix.bytes
73
72
  bytes += [version]
74
73
 
75
74
  if mask != 0
@@ -7,13 +7,13 @@ require_relative './utils/transaction'
7
7
  module MixinBot
8
8
  module Utils
9
9
  class << self
10
- MAGIC = [0x77, 0x77]
10
+ MAGIC = [0x77, 0x77].freeze
11
11
  TX_VERSION = 2
12
12
  MAX_ENCODE_INT = 0xFFFF
13
- NULL_BYTES = [0x00, 0x00]
13
+ NULL_BYTES = [0x00, 0x00].freeze
14
14
  AGGREGATED_SIGNATURE_PREFIX = 0xFF01
15
- AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00]
16
- AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01]
15
+ AGGREGATED_SIGNATURE_ORDINAY_MASK = [0x00].freeze
16
+ AGGREGATED_SIGNATURE_SPARSE_MASK = [0x01].freeze
17
17
 
18
18
  def generate_unique_uuid(uuid_1, uuid_2)
19
19
  md5 = Digest::MD5.new
@@ -53,9 +53,7 @@ module MixinBot
53
53
  end
54
54
 
55
55
  def sign_raw_transaction(tx)
56
- if tx.is_a? String
57
- tx = JSON.parse tx
58
- end
56
+ tx = JSON.parse tx if tx.is_a? String
59
57
  raise ArgumentError, "#{tx} is not a valid json" unless tx.is_a? Hash
60
58
 
61
59
  tx = tx.with_indifferent_access
@@ -64,7 +62,7 @@ module MixinBot
64
62
  asset: tx[:asset],
65
63
  inputs: tx[:inputs],
66
64
  outputs: tx[:outputs],
67
- extra: tx[:extra],
65
+ extra: tx[:extra]
68
66
  ).encode.hex
69
67
  end
70
68
 
@@ -74,15 +72,15 @@ module MixinBot
74
72
 
75
73
  def nft_memo(collection, token, extra)
76
74
  MixinBot::Utils::Nfo.new(
77
- collection: collection,
78
- token: token,
75
+ collection: collection,
76
+ token: token,
79
77
  extra: extra
80
78
  ).mint_memo
81
79
  end
82
80
 
83
81
  def encode_int(int)
84
82
  raise ArgumentError, "only support int #{int}" unless int.is_a?(Integer)
85
- raise ArgumentError,"int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
83
+ raise ArgumentError, "int #{int} is larger than MAX_ENCODE_INT #{MAX_ENCODE_INT}" if int > MAX_ENCODE_INT
86
84
 
87
85
  [int].pack('S*').bytes.reverse
88
86
  end
@@ -97,8 +95,9 @@ module MixinBot
97
95
  bytes = []
98
96
  loop do
99
97
  break if int === 0
98
+
100
99
  bytes.push int & 255
101
- int = int / (2 ** 8) | 0
100
+ int = int / (2**8) | 0
102
101
  end
103
102
 
104
103
  bytes.reverse
@@ -107,7 +106,7 @@ module MixinBot
107
106
  def bytes_to_int(bytes)
108
107
  int = 0
109
108
  bytes.each do |byte|
110
- int = int * (2 ** 8) + byte
109
+ int = int * (2**8) + byte
111
110
  end
112
111
 
113
112
  int
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MixinBot
4
- VERSION = '0.9.0'
4
+ VERSION = '0.10.0'
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.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - an-lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-08 00:00:00.000000000 Z
11
+ date: 2022-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -281,6 +281,7 @@ files:
281
281
  - lib/mixin_bot/api/blaze.rb
282
282
  - lib/mixin_bot/api/collectible.rb
283
283
  - lib/mixin_bot/api/conversation.rb
284
+ - lib/mixin_bot/api/encrypted_message.rb
284
285
  - lib/mixin_bot/api/me.rb
285
286
  - lib/mixin_bot/api/message.rb
286
287
  - lib/mixin_bot/api/multisig.rb