mixin_bot 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mixin_bot/api/collectible.rb +38 -3
- data/lib/mixin_bot/api/multisig.rb +9 -18
- data/lib/mixin_bot/api.rb +15 -0
- data/lib/mixin_bot/utils.rb +206 -6
- data/lib/mixin_bot/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fea4c2a8e738f32bcadb126f775042eb9c0a57d9fcfd168948127e8332e58a9a
|
4
|
+
data.tar.gz: 9f0699d3a105c499530230b565b6c22a8cd74dcda857a220fb6de73a899f1ffe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 774bf51ca38c7e507e4e6da343e139500715d7c150fb04083f4418e9a3a8fe6627383230953807f7463cc041391e8649c01290e634783b6e04b47f150eb01fe6
|
7
|
+
data.tar.gz: 69732a0f23244b8c62aa4fd553af66239df418427c9f7b0afd1437cd8821a9d05df3ac8c1ad4d60c66559b613fce5dc4268e67b5122bae7d7aeff20273a301ce
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module MixinBot
|
4
4
|
class API
|
5
5
|
module Collectible
|
6
|
+
NFT_ASSET_MIXIN_ID = '1700941284a95f31b25ec8c546008f208f88eee4419ccdcdbe6e3195e60128ca'
|
7
|
+
|
6
8
|
def collectible(id, access_token: nil)
|
7
9
|
path = "/collectibles/tokens/#{id}"
|
8
10
|
access_token ||= access_token('GET', path, '')
|
@@ -35,7 +37,7 @@ module MixinBot
|
|
35
37
|
|
36
38
|
COLLECTABLE_REQUEST_ACTIONS = %i[sign unlock].freeze
|
37
39
|
def create_collectible_request(action, raw, access_token: nil)
|
38
|
-
raise ArgumentError, "request action is limited in #{COLLECTABLE_REQUEST_ACTIONS.join(', ')}" unless action.to_sym
|
40
|
+
raise ArgumentError, "request action is limited in #{COLLECTABLE_REQUEST_ACTIONS.join(', ')}" unless COLLECTABLE_REQUEST_ACTIONS.include? action.to_sym
|
39
41
|
path = '/collectibles/requests'
|
40
42
|
payload = {
|
41
43
|
action: action,
|
@@ -47,11 +49,11 @@ module MixinBot
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def create_sign_collectible_request(raw, access_token: nil)
|
50
|
-
|
52
|
+
create_collectible_request 'sign', raw, access_token: access_token
|
51
53
|
end
|
52
54
|
|
53
55
|
def create_unlock_collectible_request(raw, access_token: nil)
|
54
|
-
|
56
|
+
create_collectible_request 'unlock', raw, access_token: access_token
|
55
57
|
end
|
56
58
|
|
57
59
|
def sign_collectible_request(request_id, pin)
|
@@ -85,6 +87,39 @@ module MixinBot
|
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
90
|
+
# collectible = {
|
91
|
+
# type: 'non_fungible_output',
|
92
|
+
# user_id: '',
|
93
|
+
# output_id: '',
|
94
|
+
# token_id: '',
|
95
|
+
# transaction_hash: '',
|
96
|
+
# output_index: '',
|
97
|
+
# amount: 1,
|
98
|
+
# senders: [],
|
99
|
+
# sender_threshold: 1,
|
100
|
+
# receivers: [],
|
101
|
+
# receivers_threshold: 1,
|
102
|
+
# state: 'unspent'
|
103
|
+
# }
|
104
|
+
COLLECTIBLE_TRANSACTION_ARGUMENTS = %i[collectible nfo receivers threshold].freeze
|
105
|
+
def build_collectible_transaction(**kwargs)
|
106
|
+
raise ArgumentError, "#{COLLECTIBLE_TRANSACTION_ARGUMENTS.join(', ')} are needed for build collectible transaction" unless COLLECTIBLE_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
|
107
|
+
|
108
|
+
kwargs = kwargs.with_indifferent_access
|
109
|
+
collectible = kwargs['collectible']
|
110
|
+
raise "collectible is #{collectible['state']}" unless collectible['state'] == 'unspent'
|
111
|
+
|
112
|
+
build_raw_transaction(
|
113
|
+
utxos: [collectible],
|
114
|
+
senders: collectible['receivers'],
|
115
|
+
receivers: kwargs['receivers'],
|
116
|
+
threshold: kwargs['threshold'],
|
117
|
+
extra: kwargs["nfo"],
|
118
|
+
amount: 1,
|
119
|
+
asset_mixin_id: NFT_ASSET_MIXIN_ID
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
88
123
|
def nft_memo(collection, token_id, meta)
|
89
124
|
MixinBot::Utils.nft_memo collection, token_id, meta
|
90
125
|
end
|
@@ -40,7 +40,7 @@ module MixinBot
|
|
40
40
|
|
41
41
|
MULTISIG_REQUEST_ACTIONS = %i[sign unlock].freeze
|
42
42
|
def create_multisig_request(action, raw, access_token: nil)
|
43
|
-
raise ArgumentError, "request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}" unless action.to_sym
|
43
|
+
raise ArgumentError, "request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}" unless MULTISIG_REQUEST_ACTIONS.include? action.to_sym
|
44
44
|
|
45
45
|
path = '/multisigs/requests'
|
46
46
|
payload = {
|
@@ -54,13 +54,13 @@ module MixinBot
|
|
54
54
|
|
55
55
|
# transfer from the multisig address
|
56
56
|
def create_sign_multisig_request(raw, access_token: nil)
|
57
|
-
create_multisig_request 'sign', raw, access_token
|
57
|
+
create_multisig_request 'sign', raw, access_token: access_token
|
58
58
|
end
|
59
59
|
|
60
60
|
# transfer from the multisig address
|
61
61
|
# create a request for unlock a multi-sign
|
62
62
|
def create_unlock_multisig_request(raw, access_token: nil)
|
63
|
-
create_multisig_request 'unlock', raw, access_token
|
63
|
+
create_multisig_request 'unlock', raw, access_token: access_token
|
64
64
|
end
|
65
65
|
|
66
66
|
def sign_multisig_request(request_id, pin)
|
@@ -151,7 +151,7 @@ module MixinBot
|
|
151
151
|
# amount: string / float,
|
152
152
|
# memo: string,
|
153
153
|
# }
|
154
|
-
RAW_TRANSACTION_ARGUMENTS = %i[senders receivers amount threshold
|
154
|
+
RAW_TRANSACTION_ARGUMENTS = %i[utxos senders receivers amount threshold].freeze
|
155
155
|
def build_raw_transaction(**kwargs)
|
156
156
|
raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
|
157
157
|
|
@@ -160,24 +160,14 @@ module MixinBot
|
|
160
160
|
amount = kwargs[:amount]
|
161
161
|
threshold = kwargs[:threshold]
|
162
162
|
asset_id = kwargs[:asset_id]
|
163
|
+
asset_mixin_id = kwargs[:asset_mixin_id]
|
163
164
|
utxos = kwargs[:utxos]
|
164
165
|
memo = kwargs[:memo]
|
166
|
+
extra = kwargs[:extra]
|
165
167
|
access_token = kwargs[:access_token]
|
166
168
|
|
167
169
|
raise 'access_token required!' if access_token.nil? && !senders.include?(client_id)
|
168
170
|
|
169
|
-
# default to use all(first 100) unspent utxo
|
170
|
-
utxos ||= multisigs(
|
171
|
-
members: senders,
|
172
|
-
threshold: threshold,
|
173
|
-
state: 'unspent',
|
174
|
-
access_token: access_token
|
175
|
-
)['data'].filter(
|
176
|
-
&lambda { |utxo|
|
177
|
-
utxo['asset_id'] == kwargs[:asset_id]
|
178
|
-
}
|
179
|
-
)
|
180
|
-
|
181
171
|
amount = amount.to_f.round(8)
|
182
172
|
input_amount = utxos.map(
|
183
173
|
&lambda { |utxo|
|
@@ -221,10 +211,11 @@ module MixinBot
|
|
221
211
|
}
|
222
212
|
end
|
223
213
|
|
224
|
-
extra = Digest.hexencode
|
214
|
+
extra = extra || Digest.hexencode(memo.to_s.slice(0, 140))
|
215
|
+
asset = asset_mixin_id || SHA3::Digest::SHA256.hexdigest(asset_id)
|
225
216
|
tx = {
|
226
217
|
version: 2,
|
227
|
-
asset:
|
218
|
+
asset: asset,
|
228
219
|
inputs: inputs,
|
229
220
|
outputs: outputs,
|
230
221
|
extra: extra
|
data/lib/mixin_bot/api.rb
CHANGED
@@ -48,6 +48,10 @@ module MixinBot
|
|
48
48
|
MixinBot::Utils.sign_raw_transaction tx
|
49
49
|
end
|
50
50
|
|
51
|
+
def decode_raw_transaction(raw)
|
52
|
+
MixinBot::Utils.decode_raw_transaction raw
|
53
|
+
end
|
54
|
+
|
51
55
|
# Use a mixin software to implement transaction build
|
52
56
|
def sign_raw_transaction_native(json)
|
53
57
|
ensure_mixin_command_exist
|
@@ -59,6 +63,17 @@ module MixinBot
|
|
59
63
|
output.chomp
|
60
64
|
end
|
61
65
|
|
66
|
+
# Use a mixin software to implement transaction build
|
67
|
+
def decode_raw_transaction_native(raw)
|
68
|
+
ensure_mixin_command_exist
|
69
|
+
command = format("mixin decoderawtransaction --raw '%<arg>s'", arg: raw)
|
70
|
+
|
71
|
+
output, error = Open3.capture3(command)
|
72
|
+
raise error unless error.empty?
|
73
|
+
|
74
|
+
JSON.parse output.chomp
|
75
|
+
end
|
76
|
+
|
62
77
|
include MixinBot::API::App
|
63
78
|
include MixinBot::API::Asset
|
64
79
|
include MixinBot::API::Attachment
|
data/lib/mixin_bot/utils.rb
CHANGED
@@ -58,6 +58,7 @@ module MixinBot
|
|
58
58
|
if tx.is_a? String
|
59
59
|
tx = JSON.parse tx
|
60
60
|
end
|
61
|
+
raise "#{tx} is not a valid json" unless tx.is_a? Hash
|
61
62
|
|
62
63
|
tx = tx.with_indifferent_access
|
63
64
|
bytes = []
|
@@ -67,6 +68,8 @@ module MixinBot
|
|
67
68
|
|
68
69
|
# version
|
69
70
|
bytes += [0, tx['version']]
|
71
|
+
|
72
|
+
# asset
|
70
73
|
bytes += [tx['asset']].pack('H*').bytes
|
71
74
|
|
72
75
|
# inputs
|
@@ -92,6 +95,72 @@ module MixinBot
|
|
92
95
|
bytes.pack('C*').unpack1('H*')
|
93
96
|
end
|
94
97
|
|
98
|
+
def decode_raw_transaction(raw)
|
99
|
+
bytes = [raw].pack('H*').bytes
|
100
|
+
tx = {}
|
101
|
+
|
102
|
+
magic = bytes.shift(2)
|
103
|
+
raise 'Not valid raw' unless magic == MAGIC
|
104
|
+
|
105
|
+
version = bytes.shift(2)
|
106
|
+
tx['version'] = bytes_to_int version
|
107
|
+
|
108
|
+
asset = bytes.shift(32)
|
109
|
+
tx['asset'] = asset.pack('C*').unpack1('H*')
|
110
|
+
|
111
|
+
# read inputs
|
112
|
+
bytes, tx = decode_inputs bytes, tx
|
113
|
+
|
114
|
+
# read outputs
|
115
|
+
bytes, tx = decode_outputs bytes, tx
|
116
|
+
|
117
|
+
extra_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
118
|
+
tx['extra'] = bytes.shift(extra_size).pack('C*').unpack1('H*')
|
119
|
+
|
120
|
+
num = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
121
|
+
if num == MAX_ENCODE_INT
|
122
|
+
# aggregated
|
123
|
+
aggregated = {}
|
124
|
+
|
125
|
+
raise 'invalid aggregated' unless bytes.shift(2).reverse.pack('C*').unpack1('S*') == AGGREGATED_SIGNATURE_PREFIX
|
126
|
+
|
127
|
+
aggregated['signature'] = bytes.shift(64).pack('C*').unpack1('H*')
|
128
|
+
|
129
|
+
byte = bytes.shift
|
130
|
+
case byte
|
131
|
+
when AGGREGATED_SIGNATURE_ORDINAY_MASK.first
|
132
|
+
aggregated['signers'] = []
|
133
|
+
masks_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
134
|
+
masks = bytes.shift(masks_size)
|
135
|
+
masks = [masks] unless masks.is_a? Array
|
136
|
+
|
137
|
+
masks.each_with_index do |mask, i|
|
138
|
+
8.times do |j|
|
139
|
+
k = 1 << j
|
140
|
+
aggregated['signers'].push(i * 8 + j) if mask & k == k
|
141
|
+
end
|
142
|
+
end
|
143
|
+
when AGGREGATED_SIGNATURE_SPARSE_MASK.first
|
144
|
+
signers_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
145
|
+
return if signers_size == 0
|
146
|
+
|
147
|
+
aggregated['signers'] = []
|
148
|
+
signers_size.times do
|
149
|
+
aggregated['signers'].push bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
tx['aggregated'] = aggregated
|
154
|
+
else
|
155
|
+
if !bytes.empty? && bytes[...2] != NULL_BYTES
|
156
|
+
signatures_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
157
|
+
tx['signatures'] = bytes.shift(signatures_size).pack('C*').unpack1('H*')
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
tx
|
162
|
+
end
|
163
|
+
|
95
164
|
def nft_memo_hash(collection, token_id, meta)
|
96
165
|
collection = NULL_UUID if collection.empty?
|
97
166
|
meta = meta.to_json if meta.is_a?(Hash)
|
@@ -225,9 +294,9 @@ module MixinBot
|
|
225
294
|
# genesis
|
226
295
|
genesis = input['genesis'] || ''
|
227
296
|
if genesis.empty?
|
228
|
-
bytes +=
|
297
|
+
bytes += NULL_BYTES
|
229
298
|
else
|
230
|
-
genesis_bytes = [genesis].pack('H*')
|
299
|
+
genesis_bytes = [genesis].pack('H*').bytes
|
231
300
|
bytes += encode_int genesis_bytes.size
|
232
301
|
bytes += genesis_bytes
|
233
302
|
end
|
@@ -265,7 +334,7 @@ module MixinBot
|
|
265
334
|
# group
|
266
335
|
group = mint['group'] || ''
|
267
336
|
if group.empty?
|
268
|
-
bytes += encode_int
|
337
|
+
bytes += encode_int NULL_BYTES
|
269
338
|
else
|
270
339
|
group_bytes = [group].pack('H*')
|
271
340
|
bytes += encode_int group_bytes.size
|
@@ -349,14 +418,14 @@ module MixinBot
|
|
349
418
|
end
|
350
419
|
|
351
420
|
def encode_aggregated_signature(aggregated, bytes = [])
|
352
|
-
bytes += MAX_ENCODE_INT
|
421
|
+
bytes += encode_int MAX_ENCODE_INT
|
353
422
|
bytes += encode_int AGGREGATED_SIGNATURE_PREFIX
|
354
423
|
bytes += [aggregated['signature']].pack('H*').bytes
|
355
424
|
|
356
425
|
signers = aggregated['signers']
|
357
426
|
if signers.size == 0
|
358
427
|
bytes += AGGREGATED_SIGNATURE_ORDINAY_MASK
|
359
|
-
bytes +=
|
428
|
+
bytes += NULL_BYTES
|
360
429
|
else
|
361
430
|
signers.each do |sig, i|
|
362
431
|
raise 'signers not sorted' if i > 0 && sig <= signers[i - 1]
|
@@ -366,7 +435,7 @@ module MixinBot
|
|
366
435
|
max = signers.last
|
367
436
|
if (((max / 8 | 0) + 1 | 0) > aggregated['signature'].size * 2)
|
368
437
|
bytes += AGGREGATED_SIGNATURE_SPARSE_MASK
|
369
|
-
bytes += encode_int aggregated['
|
438
|
+
bytes += encode_int aggregated['signers'].size
|
370
439
|
signers.map(&->(signer) { bytes += encode_int(signer) })
|
371
440
|
end
|
372
441
|
|
@@ -403,6 +472,137 @@ module MixinBot
|
|
403
472
|
|
404
473
|
bytes
|
405
474
|
end
|
475
|
+
|
476
|
+
def decode_inputs(bytes, tx)
|
477
|
+
inputs_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
478
|
+
tx['inputs'] = []
|
479
|
+
inputs_size.times do
|
480
|
+
input = {}
|
481
|
+
hash = bytes.shift(32)
|
482
|
+
input['hash'] = hash.pack('C*').unpack1('H*')
|
483
|
+
|
484
|
+
index = bytes.shift(2)
|
485
|
+
input['index'] = index.reverse.pack('C*').unpack1('S*')
|
486
|
+
|
487
|
+
if bytes[...2] != NULL_BYTES
|
488
|
+
genesis_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
489
|
+
genesis = bytes.shift(genesis_size)
|
490
|
+
input['genesis'] = genesis.pack('C*').unpack1('H*')
|
491
|
+
else
|
492
|
+
bytes.shift 2
|
493
|
+
end
|
494
|
+
|
495
|
+
if bytes[...2] != NULL_BYTES
|
496
|
+
magic = bytes.shift(2)
|
497
|
+
raise 'Not valid input' unless magic == MAGIC
|
498
|
+
|
499
|
+
deposit = {}
|
500
|
+
deposit['chain'] = bytes.shift(32).pack('C*').unpack1('H*')
|
501
|
+
|
502
|
+
asset_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
503
|
+
deposit['asset'] = bytes.shift(asset_size).unpack1('H*')
|
504
|
+
|
505
|
+
transaction_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
506
|
+
deposit['transaction'] = bytes.shift(transaction_size).unpack1('H*')
|
507
|
+
|
508
|
+
deposit['index'] = bytes.shift(8).reverse.pack('C*').unpack1('Q*')
|
509
|
+
|
510
|
+
amount_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
511
|
+
deposit['amount'] = bytes_to_int bytes.shift(amount_size)
|
512
|
+
|
513
|
+
input['deposit'] = deposit
|
514
|
+
else
|
515
|
+
bytes.shift 2
|
516
|
+
end
|
517
|
+
|
518
|
+
if bytes[...2] != NULL_BYTES
|
519
|
+
magic = bytes.shift(2)
|
520
|
+
raise 'Not valid input' unless magic == MAGIC
|
521
|
+
|
522
|
+
mint = {}
|
523
|
+
if bytes[...2] != NULL_BYTES
|
524
|
+
group_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
525
|
+
mint['group'] = bytes.shift(group_size).unpack1('H*')
|
526
|
+
else
|
527
|
+
bytes.shift 2
|
528
|
+
end
|
529
|
+
|
530
|
+
mint['batch'] = bytes.shift(8).reverse.pack('C*').unpack1('Q*')
|
531
|
+
_amount_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
532
|
+
mint['amount'] = bytes_to_int bytes.shift(_amount_size)
|
533
|
+
|
534
|
+
input['mint'] = mint
|
535
|
+
else
|
536
|
+
bytes.shift 2
|
537
|
+
end
|
538
|
+
|
539
|
+
tx['inputs'].push input
|
540
|
+
end
|
541
|
+
|
542
|
+
[bytes, tx]
|
543
|
+
end
|
544
|
+
|
545
|
+
def decode_outputs(bytes, tx)
|
546
|
+
outputs_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
547
|
+
tx['outputs'] = []
|
548
|
+
outputs_size.times do
|
549
|
+
output = {}
|
550
|
+
|
551
|
+
bytes.shift
|
552
|
+
type = bytes.shift
|
553
|
+
output['type'] = type
|
554
|
+
|
555
|
+
amount_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
556
|
+
output['amount'] = format('%.8f', bytes_to_int(bytes.shift(amount_size)).to_f / 1e8)
|
557
|
+
|
558
|
+
output['keys'] = []
|
559
|
+
keys_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
560
|
+
keys_size.times do
|
561
|
+
output['keys'].push bytes.shift(32).pack('C*').unpack1('H*')
|
562
|
+
end
|
563
|
+
|
564
|
+
output['mask'] = bytes.shift(32).pack('C*').unpack1('H*')
|
565
|
+
|
566
|
+
script_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
567
|
+
output['script'] = bytes.shift(script_size).pack('C*').unpack1('H*')
|
568
|
+
|
569
|
+
if bytes[...2] != NULL_BYTES
|
570
|
+
magic = bytes.shift(2)
|
571
|
+
raise 'Not valid output' unless magic == MAGIC
|
572
|
+
|
573
|
+
withdraw = {}
|
574
|
+
|
575
|
+
output['chain'] = bytes.shift(32).pack('C*').unpack1('H*')
|
576
|
+
|
577
|
+
asset_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
578
|
+
output['asset'] = bytes.shift(asset_size).unpack1('H*')
|
579
|
+
|
580
|
+
if bytes[...2] != NULL_BYTES
|
581
|
+
address = {}
|
582
|
+
|
583
|
+
adderss_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
584
|
+
output['adderss'] = bytes.shift(adderss_size).pack('C*').unpack1('H*')
|
585
|
+
else
|
586
|
+
bytes.shift 2
|
587
|
+
end
|
588
|
+
|
589
|
+
if bytes[...2] != NULL_BYTES
|
590
|
+
tag = {}
|
591
|
+
|
592
|
+
tag_size = bytes.shift(2).reverse.pack('C*').unpack1('S*')
|
593
|
+
output['tag'] = bytes.shift(tag_size).pack('C*').unpack1('H*')
|
594
|
+
else
|
595
|
+
bytes.shift 2
|
596
|
+
end
|
597
|
+
else
|
598
|
+
bytes.shift 2
|
599
|
+
end
|
600
|
+
|
601
|
+
tx['outputs'].push output
|
602
|
+
end
|
603
|
+
|
604
|
+
[bytes, tx]
|
605
|
+
end
|
406
606
|
end
|
407
607
|
end
|
408
608
|
end
|
data/lib/mixin_bot/version.rb
CHANGED
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.6.
|
4
|
+
version: 0.6.1
|
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-10-
|
11
|
+
date: 2021-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|