mixin_bot 0.1.1 → 0.1.3
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.
- checksums.yaml +4 -4
- data/lib/mixin_bot.rb +1 -0
- data/lib/mixin_bot/api.rb +4 -0
- data/lib/mixin_bot/api/attachment.rb +32 -0
- data/lib/mixin_bot/api/auth.rb +1 -1
- data/lib/mixin_bot/api/blaze.rb +62 -0
- data/lib/mixin_bot/api/conversation.rb +4 -3
- data/lib/mixin_bot/api/message.rb +28 -3
- data/lib/mixin_bot/api/pin.rb +9 -10
- data/lib/mixin_bot/api/snapshot.rb +16 -13
- data/lib/mixin_bot/api/transfer.rb +1 -1
- data/lib/mixin_bot/api/user.rb +1 -1
- data/lib/mixin_bot/api/withdraw.rb +2 -2
- data/lib/mixin_bot/version.rb +1 -1
- metadata +51 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d35e63d1229361b23be1356cd0d341662c5e33838dd0ba3f37afacd182c61dd4
|
4
|
+
data.tar.gz: b737b9d7ad6544ab5271433a920a0c37fdef126a9d0d6e2c730d65b9b40a4c6a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5993dfafb5b91bdcf5b568f5458f51ce46911be312af5a3faa3fba221b073e3acfea037f5bd403eab952fc9790dd420d712a2321ec48583d5dd9a2b4a3ec657e
|
7
|
+
data.tar.gz: ebda3aeac025cf3e27840e6ac082cce436d87292ec22efd23aea7667c89c5b7e84ac83107125ff828df586c397f4511d8285cdd2cf0acf527d6ac2c2f1779448
|
data/lib/mixin_bot.rb
CHANGED
data/lib/mixin_bot/api.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
require_relative './client'
|
4
4
|
require_relative './errors'
|
5
|
+
require_relative './api/attachment'
|
5
6
|
require_relative './api/auth'
|
7
|
+
require_relative './api/blaze'
|
6
8
|
require_relative './api/conversation'
|
7
9
|
require_relative './api/me'
|
8
10
|
require_relative './api/message'
|
@@ -27,7 +29,9 @@ module MixinBot
|
|
27
29
|
@client = Client.new
|
28
30
|
end
|
29
31
|
|
32
|
+
include MixinBot::API::Attachment
|
30
33
|
include MixinBot::API::Auth
|
34
|
+
include MixinBot::API::Blaze
|
31
35
|
include MixinBot::API::Conversation
|
32
36
|
include MixinBot::API::Me
|
33
37
|
include MixinBot::API::Message
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MixinBot
|
4
|
+
class API
|
5
|
+
module Attachment
|
6
|
+
# https://developers.mixin.one/api/beta-mixin-message/create-attachment/
|
7
|
+
# Sample Response
|
8
|
+
# {
|
9
|
+
# "data":{
|
10
|
+
# "type":"attachment",
|
11
|
+
# "attachment_id":"7a54e394-1626-4cd4-b967-543932c2a032",
|
12
|
+
# "upload_url":"https://moments-shou-tv.s3.amazonaws.com/mixin/attachments/xxx",
|
13
|
+
# "view_url":"https://moments.shou.tv/mixin/attachments/1526305123xxxx"
|
14
|
+
# }
|
15
|
+
# }
|
16
|
+
# Once get the upload_url, use it to upload the your file via PUT request
|
17
|
+
def create_attachment
|
18
|
+
path = '/attachments'
|
19
|
+
access_token ||= access_token('POST', path, {}.to_json)
|
20
|
+
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
21
|
+
client.post(path, headers: { 'Authorization': authorization }, json: {})
|
22
|
+
end
|
23
|
+
|
24
|
+
def read_attachment(attachment_id)
|
25
|
+
path = format('/attachments/%<id>s', id: attachment_id)
|
26
|
+
access_token ||= access_token('GET', path, '')
|
27
|
+
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
28
|
+
client.get(path, headers: { 'Authorization': authorization })
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/mixin_bot/api/auth.rb
CHANGED
@@ -34,7 +34,7 @@ module MixinBot
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def request_oauth(scope = nil)
|
37
|
-
scope ||= (MixinBot.scope || 'PROFILE:READ
|
37
|
+
scope ||= (MixinBot.scope || 'PROFILE:READ')
|
38
38
|
format(
|
39
39
|
'https://mixin.one/oauth/authorize?client_id=%<client_id>s&scope=%<scope>s',
|
40
40
|
client_id: client_id,
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MixinBot
|
4
|
+
class API
|
5
|
+
module Blaze
|
6
|
+
def blaze
|
7
|
+
access_token = access_token('GET', '/', '')
|
8
|
+
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
9
|
+
Faye::WebSocket::Client.new(
|
10
|
+
'wss://blaze.mixin.one/',
|
11
|
+
['Mixin-Blaze-1'],
|
12
|
+
headers: { 'Authorization' => authorization },
|
13
|
+
ping: 60
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def start_blaze_connnect(reconnect = true, &_block)
|
18
|
+
ws ||= blaze
|
19
|
+
yield if block_given?
|
20
|
+
|
21
|
+
ws.on :open do |event|
|
22
|
+
if defined? on_open
|
23
|
+
on_open ws, event
|
24
|
+
else
|
25
|
+
p [Time.now.to_s, :open]
|
26
|
+
ws.send list_pending_message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
ws.on :message do |event|
|
31
|
+
if defined? on_message
|
32
|
+
on_message ws, event
|
33
|
+
else
|
34
|
+
raw = JSON.parse read_ws_message(event.data)
|
35
|
+
p [Time.now.to_s, :message, raw&.[]('action')]
|
36
|
+
|
37
|
+
ws.send acknowledge_message_receipt(raw['data']['message_id']) unless raw&.[]('data')&.[]('message_id').nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
ws.on :error do |event|
|
42
|
+
if defined? on_error
|
43
|
+
on_error ws, event
|
44
|
+
else
|
45
|
+
p [Time.now.to_s, :error]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ws.on :close do |event|
|
50
|
+
if defined? on_close
|
51
|
+
on_close ws, event
|
52
|
+
else
|
53
|
+
p [Time.now.to_s, :close, event.code, event.reason]
|
54
|
+
end
|
55
|
+
|
56
|
+
ws = nil
|
57
|
+
start_blaze_connnect { yield } if reconnect
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -33,10 +33,11 @@ module MixinBot
|
|
33
33
|
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
34
34
|
end
|
35
35
|
|
36
|
-
def unique_conversation_id(user_id)
|
36
|
+
def unique_conversation_id(user_id, opponent_id = nil)
|
37
|
+
opponent_id ||= client_id
|
37
38
|
md5 = Digest::MD5.new
|
38
|
-
md5 << [user_id,
|
39
|
-
md5 << [user_id,
|
39
|
+
md5 << [user_id, opponent_id].min
|
40
|
+
md5 << [user_id, opponent_id].max
|
40
41
|
digest = md5.digest
|
41
42
|
digest6 = (digest[6].ord & 0x0f | 0x30).chr
|
42
43
|
digest8 = (digest[8].ord & 0x3f | 0x80).chr
|
@@ -203,11 +203,33 @@ module MixinBot
|
|
203
203
|
base_message_params(options)
|
204
204
|
end
|
205
205
|
|
206
|
+
def recall_message_params(message_id, options)
|
207
|
+
raise 'recipient_id is required!' if options[:recipient_id].nil?
|
208
|
+
|
209
|
+
options.merge!(
|
210
|
+
category: 'MESSAGE_RECALL',
|
211
|
+
data: {
|
212
|
+
message_id: message_id
|
213
|
+
}
|
214
|
+
)
|
215
|
+
base_message_params(options)
|
216
|
+
end
|
217
|
+
|
206
218
|
# base format of message params
|
207
|
-
def base_message_params(
|
219
|
+
def base_message_params(
|
220
|
+
conversation_id:,
|
221
|
+
category:,
|
222
|
+
data:,
|
223
|
+
quote_message_id: nil,
|
224
|
+
message_id: nil,
|
225
|
+
recipient_id: nil,
|
226
|
+
representative_id: nil
|
227
|
+
)
|
208
228
|
data = data.is_a?(String) ? data : data.to_json
|
209
229
|
{
|
210
230
|
conversation_id: conversation_id,
|
231
|
+
recipient_id: recipient_id,
|
232
|
+
representative_id: representative_id,
|
211
233
|
category: category,
|
212
234
|
status: 'SENT',
|
213
235
|
quote_message_id: quote_message_id,
|
@@ -258,6 +280,10 @@ module MixinBot
|
|
258
280
|
send_message app_button_group(options)
|
259
281
|
end
|
260
282
|
|
283
|
+
def recall_message(message_id, options)
|
284
|
+
send_message [recall_message_params(message_id, options)]
|
285
|
+
end
|
286
|
+
|
261
287
|
# {
|
262
288
|
# "id": "UUID",
|
263
289
|
# "action": "CREATE_PLAIN_MESSAGES",
|
@@ -276,9 +302,8 @@ module MixinBot
|
|
276
302
|
# ]
|
277
303
|
# }
|
278
304
|
# }
|
279
|
-
# not verified yet
|
280
305
|
def send_plain_messages(messages)
|
281
|
-
send_message
|
306
|
+
send_message messages
|
282
307
|
end
|
283
308
|
|
284
309
|
# http post request
|
data/lib/mixin_bot/api/pin.rb
CHANGED
@@ -15,14 +15,13 @@ module MixinBot
|
|
15
15
|
client.post(path, headers: { 'Authorization': authorization }, json: payload)
|
16
16
|
end
|
17
17
|
|
18
|
-
#
|
18
|
+
# TODO:
|
19
19
|
# https://developers.mixin.one/api/alpha-mixin-network/create-pin/
|
20
20
|
def update_pin(old_pin:, new_pin:)
|
21
21
|
path = '/pin/update'
|
22
|
-
timestamp = Time.now.utc.to_i
|
23
22
|
payload = {
|
24
|
-
old_pin: old_pin.nil? ? '' : encrypt_pin(old_pin
|
25
|
-
pin: encrypt_pin(new_pin
|
23
|
+
old_pin: old_pin.nil? ? '' : encrypt_pin(old_pin),
|
24
|
+
pin: encrypt_pin(new_pin)
|
26
25
|
}
|
27
26
|
|
28
27
|
access_token = access_token('POST', path, payload.to_json)
|
@@ -47,13 +46,13 @@ module MixinBot
|
|
47
46
|
|
48
47
|
# https://developers.mixin.one/api/alpha-mixin-network/encrypted-pin/
|
49
48
|
# use timestamp(timestamp) for iterator as default: must be bigger than the previous, the first time must be greater than 0. After a new session created, it will be reset to 0.
|
50
|
-
def encrypt_pin(pin_code,
|
49
|
+
def encrypt_pin(pin_code, iterator: nil)
|
51
50
|
aes_key = JOSE::JWA::PKCS1.rsaes_oaep_decrypt('SHA256', pin_token, private_key, session_id)
|
52
|
-
|
53
|
-
tszero =
|
54
|
-
tsone = (
|
55
|
-
tstwo = (
|
56
|
-
tsthree = (
|
51
|
+
iterator ||= Time.now.utc.to_i
|
52
|
+
tszero = iterator % 0x100
|
53
|
+
tsone = (iterator % 0x10000) >> 8
|
54
|
+
tstwo = (iterator % 0x1000000) >> 16
|
55
|
+
tsthree = (iterator % 0x100000000) >> 24
|
57
56
|
tsstring = tszero.chr + tsone.chr + tstwo.chr + tsthree.chr + "\0\0\0\0"
|
58
57
|
encrypt_content = pin_code + tsstring + tsstring
|
59
58
|
pad_count = 16 - encrypt_content.length % 16
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module MixinBot
|
4
4
|
class API
|
5
5
|
module Snapshot
|
6
|
-
def
|
6
|
+
def read_network_snapshots(options = {})
|
7
7
|
path = format(
|
8
8
|
'/network/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s&order=%<order>s',
|
9
9
|
limit: options[:limit],
|
@@ -11,21 +11,24 @@ module MixinBot
|
|
11
11
|
asset: options[:asset],
|
12
12
|
order: options[:order]
|
13
13
|
)
|
14
|
+
client.get(path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def read_snapshots(options = {})
|
18
|
+
path = format(
|
19
|
+
'/snapshots?limit=%<limit>s&offset=%<offset>s&asset=%<asset>s',
|
20
|
+
limit: options[:limit],
|
21
|
+
offset: options[:offset],
|
22
|
+
asset: options[:asset]
|
23
|
+
)
|
14
24
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
access_token = access_token('GET', path)
|
19
|
-
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
20
|
-
client.get(path, headers: { 'Authorization': authorization, 'Content-length': 0 })
|
21
|
-
else
|
22
|
-
# read public snapshots as default
|
23
|
-
client.get(path)
|
24
|
-
end
|
25
|
+
access_token = access_token('GET', path)
|
26
|
+
authorization = format('Bearer %<access_token>s', access_token: access_token)
|
27
|
+
client.get(path, headers: { 'Authorization': authorization })
|
25
28
|
end
|
26
29
|
|
27
|
-
def
|
28
|
-
path = format('network/snapshots/%<snapshot_id>s', snapshot_id: snapshot_id)
|
30
|
+
def read_network_snapshot(snapshot_id)
|
31
|
+
path = format('/network/snapshots/%<snapshot_id>s', snapshot_id: snapshot_id)
|
29
32
|
client.get(path)
|
30
33
|
end
|
31
34
|
end
|
data/lib/mixin_bot/api/user.rb
CHANGED
@@ -7,7 +7,7 @@ module MixinBot
|
|
7
7
|
def create_withdraw_address(options)
|
8
8
|
path = '/addresses'
|
9
9
|
encrypted_pin = encrypt_pin(options[:pin])
|
10
|
-
payload =
|
10
|
+
payload =
|
11
11
|
# for EOS withdraw, account_name & account_tag must be valid
|
12
12
|
if options[:public_key].nil?
|
13
13
|
{
|
@@ -75,4 +75,4 @@ module MixinBot
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
78
|
-
end
|
78
|
+
end
|
data/lib/mixin_bot/version.rb
CHANGED
metadata
CHANGED
@@ -1,127 +1,141 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixin_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- an-lee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bcrypt
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '3.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '3.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faye-websocket
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.10'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.10'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: http
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- - "
|
45
|
+
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
47
|
+
version: '4.1'
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- - "
|
52
|
+
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
54
|
+
version: '4.1'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: jose
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- - "
|
59
|
+
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
61
|
+
version: '1.1'
|
48
62
|
type: :runtime
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- - "
|
66
|
+
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
68
|
+
version: '1.1'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: jwt
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - "
|
73
|
+
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
75
|
+
version: '2.2'
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- - "
|
80
|
+
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
82
|
+
version: '2.2'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rake
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
|
-
- - "
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
89
|
+
version: '12.3'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
96
|
+
version: '12.3'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: rspec
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
|
-
- - "
|
101
|
+
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
103
|
+
version: '3.8'
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
|
-
- - "
|
108
|
+
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
110
|
+
version: '3.8'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rubocop
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
|
-
- - "
|
115
|
+
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
117
|
+
version: '0.72'
|
104
118
|
type: :development
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
|
-
- - "
|
122
|
+
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
124
|
+
version: '0.72'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rubocop-rspec
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
114
128
|
requirements:
|
115
|
-
- - "
|
129
|
+
- - "~>"
|
116
130
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
131
|
+
version: '1.33'
|
118
132
|
type: :development
|
119
133
|
prerelease: false
|
120
134
|
version_requirements: !ruby/object:Gem::Requirement
|
121
135
|
requirements:
|
122
|
-
- - "
|
136
|
+
- - "~>"
|
123
137
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
138
|
+
version: '1.33'
|
125
139
|
description: An API wrapper for Mixin Nexwork
|
126
140
|
email:
|
127
141
|
- an.lee.work@gmail.com
|
@@ -132,7 +146,9 @@ files:
|
|
132
146
|
- MIT-LICENSE
|
133
147
|
- lib/mixin_bot.rb
|
134
148
|
- lib/mixin_bot/api.rb
|
149
|
+
- lib/mixin_bot/api/attachment.rb
|
135
150
|
- lib/mixin_bot/api/auth.rb
|
151
|
+
- lib/mixin_bot/api/blaze.rb
|
136
152
|
- lib/mixin_bot/api/conversation.rb
|
137
153
|
- lib/mixin_bot/api/me.rb
|
138
154
|
- lib/mixin_bot/api/message.rb
|
@@ -164,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
180
|
- !ruby/object:Gem::Version
|
165
181
|
version: '0'
|
166
182
|
requirements: []
|
167
|
-
rubygems_version: 3.0.
|
183
|
+
rubygems_version: 3.0.6
|
168
184
|
signing_key:
|
169
185
|
specification_version: 4
|
170
186
|
summary: An API wrapper for Mixin Nexwork
|