mixin_bot 0.12.1 → 1.1.0
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/address.rb +21 -0
- data/lib/mixin_bot/api/app.rb +5 -11
- data/lib/mixin_bot/api/asset.rb +9 -16
- data/lib/mixin_bot/api/attachment.rb +27 -22
- data/lib/mixin_bot/api/auth.rb +34 -56
- data/lib/mixin_bot/api/blaze.rb +4 -3
- data/lib/mixin_bot/api/conversation.rb +29 -49
- data/lib/mixin_bot/api/encrypted_message.rb +19 -19
- data/lib/mixin_bot/api/inscription.rb +71 -0
- data/lib/mixin_bot/api/legacy_collectible.rb +140 -0
- data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
- data/lib/mixin_bot/api/legacy_output.rb +50 -0
- data/lib/mixin_bot/api/legacy_payment.rb +31 -0
- data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
- data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
- data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
- data/lib/mixin_bot/api/me.rb +13 -17
- data/lib/mixin_bot/api/message.rb +13 -10
- data/lib/mixin_bot/api/multisig.rb +17 -222
- data/lib/mixin_bot/api/output.rb +48 -0
- data/lib/mixin_bot/api/payment.rb +9 -20
- data/lib/mixin_bot/api/pin.rb +57 -65
- data/lib/mixin_bot/api/rpc.rb +12 -14
- data/lib/mixin_bot/api/snapshot.rb +15 -29
- data/lib/mixin_bot/api/tip.rb +43 -0
- data/lib/mixin_bot/api/transaction.rb +295 -60
- data/lib/mixin_bot/api/transfer.rb +69 -31
- data/lib/mixin_bot/api/user.rb +88 -53
- data/lib/mixin_bot/api/withdraw.rb +52 -53
- data/lib/mixin_bot/api.rb +81 -46
- data/lib/mixin_bot/cli/api.rb +149 -5
- data/lib/mixin_bot/cli/utils.rb +14 -4
- data/lib/mixin_bot/cli.rb +13 -10
- data/lib/mixin_bot/client.rb +74 -127
- data/lib/mixin_bot/configuration.rb +98 -0
- data/lib/mixin_bot/nfo.rb +174 -0
- data/lib/mixin_bot/transaction.rb +524 -0
- data/lib/mixin_bot/utils/address.rb +121 -0
- data/lib/mixin_bot/utils/crypto.rb +218 -0
- data/lib/mixin_bot/utils/decoder.rb +56 -0
- data/lib/mixin_bot/utils/encoder.rb +63 -0
- data/lib/mixin_bot/utils.rb +8 -109
- data/lib/mixin_bot/uuid.rb +41 -0
- data/lib/mixin_bot/version.rb +1 -1
- data/lib/mixin_bot.rb +39 -14
- data/lib/mvm/bridge.rb +2 -19
- data/lib/mvm/client.rb +11 -33
- data/lib/mvm/nft.rb +4 -4
- data/lib/mvm/registry.rb +9 -9
- data/lib/mvm/scan.rb +3 -5
- data/lib/mvm.rb +5 -6
- metadata +77 -103
- data/lib/mixin_bot/api/collectible.rb +0 -138
- data/lib/mixin_bot/utils/nfo.rb +0 -176
- data/lib/mixin_bot/utils/transaction.rb +0 -478
- data/lib/mixin_bot/utils/uuid.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4257bfdffc20e27ffab5ce9da23080be113a552623f5022775f3cc8a6cc6db0c
|
4
|
+
data.tar.gz: 7f29a034845b45715e33b3b51e28be996408fcff145538025a243fb74d42b482
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18e9d94005f235f827ce4e7598a2b2ddf13f48b296d9af46e4d6b2550e795c30f805c40b815352c159f7f2aa7b35c0a4e93ea0787889175c373e931720b5dc17
|
7
|
+
data.tar.gz: 56e0c4651199912fd4ed4bef7eb96cc22de1cd30842b7d8e767811ece926e880984a0f2734db19d4f1c48464c037e3b3d9c22b61be204c8a83ffd0f926203e37
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MixinBot
|
4
|
+
class API
|
5
|
+
module Address
|
6
|
+
def safe_deposit_entries(**kwargs)
|
7
|
+
path = '/safe/deposit/entries'
|
8
|
+
|
9
|
+
members = [kwargs[:members]] if kwargs[:members].is_a? String
|
10
|
+
|
11
|
+
payload = {
|
12
|
+
members:,
|
13
|
+
threshold: kwargs[:threshold] || 1,
|
14
|
+
chain_id: kwargs[:chain_id]
|
15
|
+
}
|
16
|
+
|
17
|
+
client.post path, **payload, access_token: kwargs[:access_token]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/mixin_bot/api/app.rb
CHANGED
@@ -6,25 +6,19 @@ 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
|
-
|
10
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
11
|
-
client.post(path, headers: { 'Authorization': authorization })
|
9
|
+
client.post path, access_token:
|
12
10
|
end
|
13
11
|
|
14
12
|
def remove_favorite_app(app_id, access_token: nil)
|
15
13
|
path = format('/apps/%<id>s/unfavorite', id: app_id)
|
16
14
|
|
17
|
-
|
18
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
19
|
-
client.post(path, headers: { 'Authorization': authorization })
|
15
|
+
client.post path, access_token:
|
20
16
|
end
|
21
17
|
|
22
|
-
def favorite_apps(user_id, access_token: nil)
|
23
|
-
path = format('/users/%<id>s/apps/favorite', id: user_id)
|
18
|
+
def favorite_apps(user_id = nil, access_token: nil)
|
19
|
+
path = format('/users/%<id>s/apps/favorite', id: user_id || config.app_id)
|
24
20
|
|
25
|
-
|
26
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
27
|
-
client.get(path, headers: { 'Authorization': authorization })
|
21
|
+
client.get path, access_token:
|
28
22
|
end
|
29
23
|
end
|
30
24
|
end
|
data/lib/mixin_bot/api/asset.rb
CHANGED
@@ -6,31 +6,24 @@ module MixinBot
|
|
6
6
|
# https://developers.mixin.one/api/alpha-mixin-network/read-assets/
|
7
7
|
def assets(access_token: nil)
|
8
8
|
path = '/assets'
|
9
|
-
|
10
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
11
|
-
client.get(path, headers: { 'Authorization': authorization })
|
9
|
+
client.get path, access_token:
|
12
10
|
end
|
13
|
-
alias read_assets assets
|
14
11
|
|
15
12
|
# https://developers.mixin.one/api/alpha-mixin-network/read-asset/
|
16
13
|
def asset(asset_id, access_token: nil)
|
17
|
-
path = format('/assets/%<asset_id>s', asset_id:
|
18
|
-
|
19
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
20
|
-
client.get(path, headers: { 'Authorization': authorization })
|
14
|
+
path = format('/assets/%<asset_id>s', asset_id:)
|
15
|
+
client.get path, access_token:
|
21
16
|
end
|
22
|
-
alias read_asset asset
|
23
17
|
|
24
18
|
# https://developers.mixin.one/document/wallet/api/ticker
|
25
|
-
def ticker(asset_id,
|
26
|
-
offset =
|
19
|
+
def ticker(asset_id, **kwargs)
|
20
|
+
offset = kwargs[:offset]
|
21
|
+
offset = DateTime.rfc3339(offset) if offset.is_a? String
|
27
22
|
offset = offset.rfc3339 if offset.is_a?(DateTime) || offset.is_a?(Time)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
client.get(path, headers: { 'Authorization': authorization })
|
23
|
+
|
24
|
+
path = '/ticker'
|
25
|
+
client.get path, asset_id:, offset:, access_token: kwargs[:access_token]
|
32
26
|
end
|
33
|
-
alias read_ticker ticker
|
34
27
|
end
|
35
28
|
end
|
36
29
|
end
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module MixinBot
|
4
4
|
class API
|
5
5
|
module Attachment
|
6
|
-
# https://developers.mixin.one/api/beta-mixin-message/create-attachment/
|
7
6
|
# Sample Response
|
8
7
|
# {
|
9
8
|
# "data":{
|
@@ -14,38 +13,44 @@ module MixinBot
|
|
14
13
|
# }
|
15
14
|
# }
|
16
15
|
# Once get the upload_url, use it to upload the your file via PUT request
|
17
|
-
def create_attachment
|
16
|
+
def create_attachment(access_token: nil)
|
18
17
|
path = '/attachments'
|
19
|
-
|
20
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
21
|
-
client.post(path, headers: { 'Authorization': authorization }, json: {})
|
18
|
+
client.post path, access_token:
|
22
19
|
end
|
23
20
|
|
24
21
|
def upload_attachment(file)
|
25
22
|
attachment = create_attachment['data']
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
24
|
+
url = attachment.delete('upload_url')
|
25
|
+
conn = Faraday.new(url:) do |f|
|
26
|
+
f.adapter :net_http
|
27
|
+
|
28
|
+
f.request :multipart
|
29
|
+
f.request :retry
|
30
|
+
f.response :raise_error
|
31
|
+
f.response :logger if config.debug
|
32
|
+
end
|
33
|
+
|
34
|
+
conn.put(url) do |req|
|
35
|
+
req.headers = {
|
36
|
+
'x-amz-acl': 'public-read',
|
37
|
+
'Content-Type': 'application/octet-stream'
|
38
|
+
}
|
39
|
+
req.body = Faraday::UploadIO.new(file, 'octet/stream')
|
40
|
+
|
41
|
+
if file.respond_to?(:length)
|
42
|
+
req.headers['Content-Length'] = file.length.to_s
|
43
|
+
elsif file.respond_to?(:stat)
|
44
|
+
req.headers['Content-Length'] = file.stat.size.to_s
|
45
|
+
end
|
46
|
+
end
|
40
47
|
|
41
48
|
attachment
|
42
49
|
end
|
43
50
|
|
44
|
-
def
|
51
|
+
def attachment(attachment_id, access_token: nil)
|
45
52
|
path = format('/attachments/%<id>s', id: attachment_id)
|
46
|
-
|
47
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
48
|
-
client.get(path, headers: { 'Authorization': authorization })
|
53
|
+
client.get path, access_token:
|
49
54
|
end
|
50
55
|
end
|
51
56
|
end
|
data/lib/mixin_bot/api/auth.rb
CHANGED
@@ -3,85 +3,60 @@
|
|
3
3
|
module MixinBot
|
4
4
|
class API
|
5
5
|
module Auth
|
6
|
-
def access_token(method, uri, body = '', exp_in: 600, scp: 'FULL')
|
7
|
-
sig = Digest::SHA256.hexdigest(method + uri + body.to_s)
|
8
|
-
iat = Time.now.utc.to_i
|
9
|
-
exp = (Time.now.utc + exp_in).to_i
|
10
|
-
jti = SecureRandom.uuid
|
11
|
-
payload = {
|
12
|
-
uid: client_id,
|
13
|
-
sid: session_id,
|
14
|
-
iat: iat,
|
15
|
-
exp: exp,
|
16
|
-
jti: jti,
|
17
|
-
sig: sig,
|
18
|
-
scp: scp
|
19
|
-
}
|
20
|
-
case key_type
|
21
|
-
when :ed25519
|
22
|
-
jwk = JOSE::JWK.from_okp [:Ed25519, private_key]
|
23
|
-
jws = JOSE::JWS.from({ 'alg' => 'EdDSA' })
|
24
|
-
when :rsa
|
25
|
-
jwk = JOSE::JWK.from_pem private_key
|
26
|
-
jws = JOSE::JWS.from({ 'alg' => 'RS512' })
|
27
|
-
end
|
28
|
-
|
29
|
-
jwt = JOSE::JWT.from payload
|
30
|
-
JOSE::JWT.sign(jwk, jws, jwt).compact
|
31
|
-
end
|
32
|
-
|
33
6
|
def oauth_token(code)
|
34
7
|
path = 'oauth/token'
|
35
8
|
payload = {
|
36
|
-
client_id:
|
37
|
-
client_secret: client_secret,
|
38
|
-
code:
|
9
|
+
client_id: config.app_id,
|
10
|
+
client_secret: config.client_secret,
|
11
|
+
code:
|
39
12
|
}
|
40
|
-
|
41
|
-
|
42
|
-
raise r.inspect if r['error'].present?
|
43
|
-
|
44
|
-
r['data']&.[]('access_token')
|
13
|
+
client.post path, **payload
|
45
14
|
end
|
46
15
|
|
47
16
|
def request_oauth(scope = nil)
|
48
|
-
scope ||=
|
17
|
+
scope ||= 'PROFILE:READ'
|
49
18
|
format(
|
50
|
-
'https://mixin.one/oauth/authorize?client_id=%<
|
51
|
-
|
52
|
-
scope:
|
19
|
+
'https://mixin.one/oauth/authorize?client_id=%<app_id>s&scope=%<scope>s',
|
20
|
+
app_id: config.app_id,
|
21
|
+
scope:
|
53
22
|
)
|
54
23
|
end
|
55
24
|
|
56
25
|
def authorize_code(**kwargs)
|
57
|
-
path = '/oauth/authorize'
|
58
26
|
data = authorization_data(
|
59
|
-
kwargs[:
|
27
|
+
kwargs[:app_id],
|
60
28
|
kwargs[:scope] || ['PROFILE:READ']
|
61
29
|
)
|
62
30
|
|
31
|
+
path = '/oauth/authorize'
|
32
|
+
pin = kwargs[:pin] || config.pin
|
63
33
|
payload = {
|
64
34
|
authorization_id: data['authorization_id'],
|
65
35
|
scopes: data['scopes'],
|
66
36
|
pin_base64: encrypt_pin(kwargs[:pin])
|
67
37
|
}
|
68
38
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
39
|
+
raise ArgumentError, 'pin is required' if pin.blank?
|
40
|
+
|
41
|
+
payload[:pin_base64] = if pin.size > 6
|
42
|
+
encrypt_tip_pin(pin, 'TIP:OAUTH:APPROVE:', data['scopes'], data['authorization_id'])
|
43
|
+
else
|
44
|
+
encrypt_pin(pin)
|
45
|
+
end
|
46
|
+
|
47
|
+
client.post path, **payload, access_token: kwargs[:access_token]
|
73
48
|
end
|
74
49
|
|
75
|
-
def authorization_data(
|
76
|
-
@
|
50
|
+
def authorization_data(app_id, scope = ['PROFILE:READ'])
|
51
|
+
@_app_id = app_id
|
77
52
|
@_scope = scope.join(' ')
|
78
53
|
EM.run do
|
79
54
|
start_blaze_connect do
|
80
|
-
def on_open(
|
81
|
-
|
55
|
+
def on_open(websocket, _event) # rubocop:disable Lint/NestedMethodDefinition
|
56
|
+
websocket.send write_ws_message(
|
82
57
|
action: 'REFRESH_OAUTH_CODE',
|
83
58
|
params: {
|
84
|
-
client_id: @
|
59
|
+
client_id: @_app_id,
|
85
60
|
scope: @_scope,
|
86
61
|
authorization_id: '',
|
87
62
|
code_challenge: ''
|
@@ -89,18 +64,21 @@ module MixinBot
|
|
89
64
|
)
|
90
65
|
end
|
91
66
|
|
92
|
-
def on_message(
|
93
|
-
raw = JSON.parse
|
94
|
-
@_data = raw
|
95
|
-
|
67
|
+
def on_message(websocket, event) # rubocop:disable Lint/NestedMethodDefinition
|
68
|
+
raw = JSON.parse ws_message(event.data)
|
69
|
+
@_data = raw
|
70
|
+
websocket.close
|
96
71
|
end
|
97
72
|
|
98
|
-
def on_close(
|
73
|
+
def on_close(_websocket, _event) # rubocop:disable Lint/NestedMethodDefinition
|
99
74
|
EM.stop_event_loop
|
100
75
|
end
|
101
76
|
end
|
102
77
|
end
|
103
|
-
|
78
|
+
|
79
|
+
raise MixinBot::RequestError, @_data if @_data['error'].present?
|
80
|
+
|
81
|
+
@_data['data']
|
104
82
|
end
|
105
83
|
end
|
106
84
|
end
|
data/lib/mixin_bot/api/blaze.rb
CHANGED
@@ -5,9 +5,10 @@ module MixinBot
|
|
5
5
|
module Blaze
|
6
6
|
def blaze
|
7
7
|
access_token = access_token('GET', '/', '')
|
8
|
-
|
8
|
+
|
9
|
+
authorization = format('Bearer %<access_token>s', access_token:)
|
9
10
|
Faye::WebSocket::Client.new(
|
10
|
-
format('wss://%<host>s/', host: blaze_host),
|
11
|
+
format('wss://%<host>s/', host: config.blaze_host),
|
11
12
|
['Mixin-Blaze-1'],
|
12
13
|
headers: { 'Authorization' => authorization },
|
13
14
|
ping: 60
|
@@ -31,7 +32,7 @@ module MixinBot
|
|
31
32
|
if defined? on_message
|
32
33
|
on_message ws, event
|
33
34
|
else
|
34
|
-
raw = JSON.parse
|
35
|
+
raw = JSON.parse ws_message(event.data)
|
35
36
|
p [Time.now.to_s, :message, raw&.[]('action')]
|
36
37
|
|
37
38
|
ws.send acknowledge_message_receipt(raw['data']['message_id']) unless raw&.[]('data')&.[]('message_id').nil?
|
@@ -3,77 +3,67 @@
|
|
3
3
|
module MixinBot
|
4
4
|
class API
|
5
5
|
module Conversation
|
6
|
-
def conversation(conversation_id)
|
7
|
-
path = format('/conversations/%<conversation_id>s', conversation_id:
|
8
|
-
|
9
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
10
|
-
client.get(path, headers: { 'Authorization': authorization })
|
6
|
+
def conversation(conversation_id, access_token: nil)
|
7
|
+
path = format('/conversations/%<conversation_id>s', conversation_id:)
|
8
|
+
client.get path, access_token:
|
11
9
|
end
|
12
|
-
alias read_conversation conversation
|
13
10
|
|
14
11
|
def conversation_by_user_id(user_id)
|
15
|
-
conversation_id =
|
16
|
-
|
12
|
+
conversation_id = unique_uuid user_id
|
13
|
+
conversation conversation_id
|
17
14
|
end
|
18
|
-
alias read_conversation_by_user_id conversation_by_user_id
|
19
15
|
|
20
|
-
def create_conversation(
|
16
|
+
def create_conversation(**kwargs)
|
21
17
|
path = '/conversations'
|
22
18
|
payload = {
|
23
|
-
category: category,
|
24
|
-
conversation_id: conversation_id || SecureRandom.uuid,
|
25
|
-
name: name,
|
26
|
-
participants: participants
|
27
|
-
}
|
19
|
+
category: kwargs[:category],
|
20
|
+
conversation_id: kwargs[:conversation_id] || SecureRandom.uuid,
|
21
|
+
name: kwargs[:name],
|
22
|
+
participants: kwargs[:participants]
|
23
|
+
}.compact_blank
|
28
24
|
|
29
|
-
|
30
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
31
|
-
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
25
|
+
client.post path, **payload, access_token: kwargs[:access_token]
|
32
26
|
end
|
33
27
|
|
34
28
|
def create_group_conversation(user_ids:, name:, conversation_id: nil, access_token: nil)
|
35
29
|
create_conversation(
|
36
30
|
category: 'GROUP',
|
37
|
-
conversation_id
|
38
|
-
name
|
31
|
+
conversation_id:,
|
32
|
+
name:,
|
39
33
|
participants: user_ids.map(&->(participant) { { user_id: participant } }),
|
40
|
-
access_token:
|
34
|
+
access_token:
|
41
35
|
)
|
42
36
|
end
|
43
37
|
|
44
38
|
def create_contact_conversation(user_id, access_token: nil)
|
45
39
|
create_conversation(
|
46
40
|
category: 'CONTACT',
|
47
|
-
conversation_id:
|
41
|
+
conversation_id: unique_uuid(user_id),
|
48
42
|
participants: [
|
49
43
|
{
|
50
|
-
user_id:
|
44
|
+
user_id:
|
51
45
|
}
|
52
46
|
],
|
53
|
-
access_token:
|
47
|
+
access_token:
|
54
48
|
)
|
55
49
|
end
|
56
50
|
|
57
51
|
def update_group_conversation_name(name:, conversation_id:, access_token: nil)
|
58
52
|
path = format('/conversations/%<id>s', id: conversation_id)
|
59
53
|
payload = {
|
60
|
-
name:
|
54
|
+
name:
|
61
55
|
}
|
62
56
|
|
63
|
-
|
64
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
65
|
-
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
57
|
+
client.post path, **payload, access_token:
|
66
58
|
end
|
67
59
|
|
68
60
|
def update_group_conversation_announcement(announcement:, conversation_id:, access_token: nil)
|
69
61
|
path = format('/conversations/%<id>s', id: conversation_id)
|
70
62
|
payload = {
|
71
|
-
announcement:
|
63
|
+
announcement:
|
72
64
|
}
|
73
65
|
|
74
|
-
|
75
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
76
|
-
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
66
|
+
client.post path, **payload, access_token:
|
77
67
|
end
|
78
68
|
|
79
69
|
# participants = [{ user_id: "" }]
|
@@ -81,9 +71,7 @@ module MixinBot
|
|
81
71
|
path = format('/conversations/%<id>s/participants/ADD', id: conversation_id)
|
82
72
|
payload = user_ids.map(&->(participant) { { user_id: participant } })
|
83
73
|
|
84
|
-
|
85
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
86
|
-
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
74
|
+
client.post path, *payload, access_token:
|
87
75
|
end
|
88
76
|
|
89
77
|
# participants = [{ user_id: "" }]
|
@@ -91,25 +79,19 @@ module MixinBot
|
|
91
79
|
path = format('/conversations/%<id>s/participants/REMOVE', id: conversation_id)
|
92
80
|
payload = user_ids.map(&->(participant) { { user_id: participant } })
|
93
81
|
|
94
|
-
|
95
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
96
|
-
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
82
|
+
client.post path, *payload, access_token:
|
97
83
|
end
|
98
84
|
|
99
85
|
def exit_conversation(conversation_id, access_token: nil)
|
100
86
|
path = format('/conversations/%<id>s/exit', id: conversation_id)
|
101
87
|
|
102
|
-
|
103
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
104
|
-
client.post(path, headers: { 'Authorization': authorization })
|
88
|
+
client.post path, access_token:
|
105
89
|
end
|
106
90
|
|
107
91
|
def rotate_conversation(conversation_id, access_token: nil)
|
108
92
|
path = format('/conversations/%<id>s/rotate', id: conversation_id)
|
109
93
|
|
110
|
-
|
111
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
112
|
-
client.post(path, headers: { 'Authorization': authorization })
|
94
|
+
client.post path, access_token:
|
113
95
|
end
|
114
96
|
|
115
97
|
# participants = [{ user_id: "", role: "ADMIN" }]
|
@@ -117,14 +99,12 @@ module MixinBot
|
|
117
99
|
path = format('/conversations/%<id>s/participants/ROLE', id: conversation_id)
|
118
100
|
payload = participants
|
119
101
|
|
120
|
-
|
121
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
122
|
-
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
102
|
+
client.post path, *payload, access_token:
|
123
103
|
end
|
124
104
|
|
125
105
|
def unique_uuid(user_id, opponent_id = nil)
|
126
|
-
opponent_id ||=
|
127
|
-
MixinBot
|
106
|
+
opponent_id ||= config.app_id
|
107
|
+
MixinBot.utils.unique_uuid user_id, opponent_id
|
128
108
|
end
|
129
109
|
alias unique_conversation_id unique_uuid
|
130
110
|
end
|
@@ -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
|
94
|
-
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]
|
108
|
-
|
109
|
-
|
110
|
-
client.post
|
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
|
-
def encrypt_message(data, sessions = [], sk: nil, pk: nil)
|
113
|
+
def encrypt_message(data, sessions = [], sk: nil, pk: nil) # rubocop:disable Naming/MethodParameterName
|
114
114
|
raise ArgumentError, 'Wrong sessions format!' unless sessions.all?(&->(s) { s.key?('session_id') && s.key?('public_key') })
|
115
115
|
|
116
|
-
sk
|
117
|
-
pk ||=
|
116
|
+
sk ||= config.session_private_key[0...32]
|
117
|
+
pk ||= config.session_private_key[32...]
|
118
118
|
|
119
|
-
|
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::
|
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
|
|
@@ -153,22 +153,22 @@ module MixinBot
|
|
153
153
|
Base64.urlsafe_encode64 bytes.pack('C*'), padding: false
|
154
154
|
end
|
155
155
|
|
156
|
-
def decrypt_message(data, sk: nil, si: nil)
|
156
|
+
def decrypt_message(data, sk: nil, si: nil) # rubocop:disable Naming/MethodParameterName
|
157
157
|
bytes = Base64.urlsafe_decode64(data).bytes
|
158
158
|
|
159
|
-
si ||= session_id
|
160
|
-
sk ||=
|
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::
|
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(
|