@rustwirebot/rustplus.js 2.5.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.
- package/LICENSE +22 -0
- package/README.md +351 -0
- package/camera.js +425 -0
- package/cli/index.js +308 -0
- package/cli/pair.html +49 -0
- package/docs/API.md +101 -0
- package/docs/DownloadBundle.md +7 -0
- package/docs/PairingFlow.md +19 -0
- package/donate.md +10 -0
- package/examples/10_shoot_autoturret.js +100 -0
- package/examples/1_send_team_chat.js +16 -0
- package/examples/2_turn_smart_switch_on.js +21 -0
- package/examples/3_turn_smart_switch_off.js +21 -0
- package/examples/4_entity_changed_broadcasts.js +58 -0
- package/examples/5_download_map_jpeg.js +22 -0
- package/examples/6_async_requests.js +35 -0
- package/examples/7_render_camera.js +34 -0
- package/examples/8_move_ptz_camera.js +56 -0
- package/examples/9_zoom_ptz_camera.js +37 -0
- package/package.json +48 -0
- package/rustplus.js +380 -0
- package/rustplus.js.svg +34 -0
- package/rustplus.proto +431 -0
- package/vendor/push-receiver/LICENSE +21 -0
- package/vendor/push-receiver/NOTICE.md +32 -0
- package/vendor/push-receiver/android/fcm.js +134 -0
- package/vendor/push-receiver/client.js +216 -0
- package/vendor/push-receiver/constants.js +53 -0
- package/vendor/push-receiver/fcm/index.js +52 -0
- package/vendor/push-receiver/fcm/server-key/index.js +67 -0
- package/vendor/push-receiver/gcm/android_checkin.proto +96 -0
- package/vendor/push-receiver/gcm/checkin.proto +155 -0
- package/vendor/push-receiver/gcm/index.js +128 -0
- package/vendor/push-receiver/index.js +17 -0
- package/vendor/push-receiver/mcs.proto +328 -0
- package/vendor/push-receiver/parser.js +276 -0
- package/vendor/push-receiver/register/index.js +18 -0
- package/vendor/push-receiver/utils/base64/index.js +15 -0
- package/vendor/push-receiver/utils/decrypt/index.js +23 -0
- package/vendor/push-receiver/utils/http-request/index.js +43 -0
- package/vendor/push-receiver/utils/request/index.js +27 -0
- package/vendor/push-receiver/utils/timeout/index.js +7 -0
package/cli/index.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
const express = require('express');
|
|
5
|
+
const { v4: uuidv4 } = require('uuid');
|
|
6
|
+
const commandLineArgs = require('command-line-args');
|
|
7
|
+
const commandLineUsage = require('command-line-usage');
|
|
8
|
+
const ChromeLauncher = require('chrome-launcher');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
// Vendored locally (see /vendor/push-receiver) so this package keeps working
|
|
12
|
+
// even if @liamcottle/push-receiver is ever removed from npm.
|
|
13
|
+
const AndroidFCM = require('../vendor/push-receiver/android/fcm');
|
|
14
|
+
const PushReceiverClient = require('../vendor/push-receiver/client');
|
|
15
|
+
|
|
16
|
+
let server;
|
|
17
|
+
let fcmClient;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the path to the config file defined in command line options,
|
|
21
|
+
* or fallback to default config file in current directory.
|
|
22
|
+
*/
|
|
23
|
+
function getConfigFile(options) {
|
|
24
|
+
return options['config-file'] || path.join(process.cwd(), 'rustplus.config.json');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Reads config file, or returns empty config on error
|
|
29
|
+
*/
|
|
30
|
+
function readConfig(configFile) {
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(fs.readFileSync(configFile));
|
|
33
|
+
} catch (err) {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Merges new config into existing config and saves to config file
|
|
40
|
+
* @param configFile
|
|
41
|
+
* @param config
|
|
42
|
+
*/
|
|
43
|
+
function updateConfig(configFile, config) {
|
|
44
|
+
// get current config
|
|
45
|
+
const currentConfig = readConfig(configFile);
|
|
46
|
+
|
|
47
|
+
// merge config into current config
|
|
48
|
+
const updatedConfig = {...currentConfig, ...config};
|
|
49
|
+
|
|
50
|
+
// convert to pretty json
|
|
51
|
+
const json = JSON.stringify(updatedConfig, null, 2);
|
|
52
|
+
|
|
53
|
+
// save updated config to config file
|
|
54
|
+
fs.writeFileSync(configFile, json, "utf8");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function getExpoPushToken(fcmToken) {
|
|
58
|
+
const response = await axios.post('https://exp.host/--/api/v2/push/getExpoPushToken', {
|
|
59
|
+
type: 'fcm',
|
|
60
|
+
deviceId: uuidv4(),
|
|
61
|
+
development: false,
|
|
62
|
+
appId: 'com.facepunch.rust.companion',
|
|
63
|
+
deviceToken: fcmToken,
|
|
64
|
+
projectId: "49451aca-a822-41e6-ad59-955718d0ff9c",
|
|
65
|
+
});
|
|
66
|
+
return response.data.data.expoPushToken;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function registerWithRustPlus(authToken, expoPushToken) {
|
|
70
|
+
return axios.post('https://companion-rust.facepunch.com:443/api/push/register', {
|
|
71
|
+
AuthToken: authToken,
|
|
72
|
+
DeviceId: 'rustplus.js',
|
|
73
|
+
PushKind: 3,
|
|
74
|
+
PushToken: expoPushToken,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function linkSteamWithRustPlus() {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const app = express();
|
|
81
|
+
|
|
82
|
+
// register pair web page
|
|
83
|
+
app.get('/', (req, res) => {
|
|
84
|
+
res.sendFile(path.join(__dirname + '/pair.html'));
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// register callback
|
|
88
|
+
app.get('/callback', async (req, res) => {
|
|
89
|
+
// we no longer need the Google Chrome instance
|
|
90
|
+
await ChromeLauncher.killAll();
|
|
91
|
+
|
|
92
|
+
// get token from callback
|
|
93
|
+
const authToken = req.query.token;
|
|
94
|
+
if (authToken) {
|
|
95
|
+
res.send('Steam Account successfully linked with rustplus.js, you can now close this window and go back to the console.');
|
|
96
|
+
resolve(authToken)
|
|
97
|
+
} else {
|
|
98
|
+
res.status(400).send('Token missing from request!');
|
|
99
|
+
reject(new Error('Token missing from request!'))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// we no longer need the express web server
|
|
103
|
+
server.close()
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Start the express server before Google Chrome is launched.
|
|
108
|
+
* If the port is updated, make sure to also update it in pair.html
|
|
109
|
+
*/
|
|
110
|
+
const port = 3000;
|
|
111
|
+
server = app.listen(port, async () => {
|
|
112
|
+
/**
|
|
113
|
+
* FIXME: Google Chrome is launched with Web Security disabled.
|
|
114
|
+
* This is bad, but it allows us to modify the window object of other domains, such as Rust+
|
|
115
|
+
* By doing so, we can inject a custom ReactNativeWebView.postMessage handler to capture Rust+ auth data.
|
|
116
|
+
*
|
|
117
|
+
* Rust+ recently changed the login flow, which no longer sends auth data in the URL callback.
|
|
118
|
+
* Auth data is now sent to a ReactNativeWebView.postMessage handler, which is available to the Rust+ app since
|
|
119
|
+
* it is a ReactNative app.
|
|
120
|
+
*
|
|
121
|
+
* We don't have access to ReactNative, but we can emulate the behaviour by registering our own window objects.
|
|
122
|
+
* However, to do so we need to disable Web Security to be able to manipulate the window of other origins.
|
|
123
|
+
*/
|
|
124
|
+
await ChromeLauncher.launch({
|
|
125
|
+
startingUrl: `http://localhost:${port}`,
|
|
126
|
+
chromeFlags: [
|
|
127
|
+
'--disable-web-security', // allows us to manipulate rust+ window
|
|
128
|
+
'--disable-popup-blocking', // allows us to open rust+ login from our main window
|
|
129
|
+
'--disable-site-isolation-trials', // required for --disable-web-security to work
|
|
130
|
+
'--user-data-dir=/tmp/temporary-chrome-profile-dir-rustplus', // create a new chrome profile for pair.js
|
|
131
|
+
],
|
|
132
|
+
handleSIGINT: false, // handled manually in shutdown
|
|
133
|
+
}).catch((error) => {
|
|
134
|
+
console.log(error);
|
|
135
|
+
console.log("pair.js failed to launch Google Chrome. Do you have it installed?");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
var expoPushToken = null;
|
|
143
|
+
var rustplusAuthToken = null;
|
|
144
|
+
|
|
145
|
+
async function fcmRegister(options) {
|
|
146
|
+
console.log("Registering with FCM");
|
|
147
|
+
const apiKey = "AIzaSyB5y2y-Tzqb4-I4Qnlsh_9naYv_TD8pCvY";
|
|
148
|
+
const projectId = "rust-companion-app";
|
|
149
|
+
const gcmSenderId = "976529667804";
|
|
150
|
+
const gmsAppId = "1:976529667804:android:d6f1ddeb4403b338fea619";
|
|
151
|
+
const androidPackageName = "com.facepunch.rust.companion";
|
|
152
|
+
const androidPackageCert = "E28D05345FB78A7A1A63D70F4A302DBF426CA5AD";
|
|
153
|
+
const fcmCredentials = await AndroidFCM.register(apiKey, projectId, gcmSenderId, gmsAppId, androidPackageName, androidPackageCert);
|
|
154
|
+
|
|
155
|
+
console.log("Fetching Expo Push Token");
|
|
156
|
+
expoPushToken = await getExpoPushToken(fcmCredentials.fcm.token).catch((error) => {
|
|
157
|
+
console.log("Failed to fetch Expo Push Token");
|
|
158
|
+
console.log(error);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// show expo push token to user
|
|
163
|
+
console.log("Successfully fetched Expo Push Token");
|
|
164
|
+
console.log("Expo Push Token: " + expoPushToken);
|
|
165
|
+
|
|
166
|
+
// tell user to link steam with rust+ through Google Chrome
|
|
167
|
+
console.log("Google Chrome is launching so you can link your Steam account with Rust+");
|
|
168
|
+
rustplusAuthToken = await linkSteamWithRustPlus();
|
|
169
|
+
|
|
170
|
+
// show rust+ auth token to user
|
|
171
|
+
console.log("Successfully linked Steam account with Rust+");
|
|
172
|
+
console.log("Rust+ AuthToken: " + rustplusAuthToken);
|
|
173
|
+
|
|
174
|
+
console.log("Registering with Rust Companion API");
|
|
175
|
+
await registerWithRustPlus(rustplusAuthToken, expoPushToken).catch((error) => {
|
|
176
|
+
console.log("Failed to register with Rust Companion API");
|
|
177
|
+
console.log(error);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
});
|
|
180
|
+
console.log("Successfully registered with Rust Companion API.");
|
|
181
|
+
|
|
182
|
+
// save to config
|
|
183
|
+
const configFile = getConfigFile(options);
|
|
184
|
+
updateConfig(configFile, {
|
|
185
|
+
fcm_credentials: fcmCredentials,
|
|
186
|
+
expo_push_token: expoPushToken,
|
|
187
|
+
rustplus_auth_token: rustplusAuthToken,
|
|
188
|
+
});
|
|
189
|
+
console.log("FCM, Expo and Rust+ auth tokens have been saved to " + configFile);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function fcmListen(options) {
|
|
193
|
+
// read config file
|
|
194
|
+
const configFile = getConfigFile(options);
|
|
195
|
+
const config = readConfig(configFile);
|
|
196
|
+
|
|
197
|
+
// make sure fcm credentials are in config
|
|
198
|
+
if(!config.fcm_credentials){
|
|
199
|
+
console.error("FCM Credentials missing. Please run `fcm-register` first.");
|
|
200
|
+
process.exit(1);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log("Listening for FCM Notifications");
|
|
205
|
+
const androidId = config.fcm_credentials.gcm.androidId;
|
|
206
|
+
const securityToken = config.fcm_credentials.gcm.securityToken;
|
|
207
|
+
const client = new PushReceiverClient(androidId, securityToken, []);
|
|
208
|
+
client.on('ON_DATA_RECEIVED', (data) => {
|
|
209
|
+
|
|
210
|
+
// generate timestamp
|
|
211
|
+
const timestamp = new Date().toLocaleString();
|
|
212
|
+
|
|
213
|
+
// log timestamp the notification was received (in green colour)
|
|
214
|
+
console.log('\x1b[32m%s\x1b[0m', `[${timestamp}] Notification Received`)
|
|
215
|
+
|
|
216
|
+
// log notification body
|
|
217
|
+
console.log(data);
|
|
218
|
+
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// force exit on ctrl + c
|
|
222
|
+
process.on('SIGINT', async () => {
|
|
223
|
+
process.exit(0);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
await client.connect();
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function showUsage() {
|
|
231
|
+
const usage = commandLineUsage([
|
|
232
|
+
{
|
|
233
|
+
header: 'RustPlus',
|
|
234
|
+
content: 'A command line tool for things related to Rust+',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
header: 'Usage',
|
|
238
|
+
content: '$ rustplus <options> <command>'
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
header: 'Command List',
|
|
242
|
+
content: [
|
|
243
|
+
{ name: 'help', summary: 'Print this usage guide.' },
|
|
244
|
+
{ name: 'fcm-register', summary: 'Registers with FCM, Expo and links your Steam account with Rust+ so you can listen for Pairing Notifications.' },
|
|
245
|
+
{ name: 'fcm-listen', summary: 'Listens to notifications received from FCM, such as Rust+ Pairing Notifications.' },
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
header: 'Options',
|
|
250
|
+
optionList: [
|
|
251
|
+
{
|
|
252
|
+
name: 'config-file',
|
|
253
|
+
typeLabel: '{underline file}',
|
|
254
|
+
description: 'Path to config file. (default: rustplus.config.json)',
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
},
|
|
258
|
+
]);
|
|
259
|
+
console.log(usage);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function run() {
|
|
263
|
+
// parse command line args
|
|
264
|
+
const options = commandLineArgs([
|
|
265
|
+
{ name: 'command', type: String, defaultOption: true },
|
|
266
|
+
{ name: 'config-file', type: String },
|
|
267
|
+
]);
|
|
268
|
+
|
|
269
|
+
// run command
|
|
270
|
+
switch(options.command){
|
|
271
|
+
case 'fcm-register': {
|
|
272
|
+
await fcmRegister(options);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 'fcm-listen': {
|
|
276
|
+
await fcmListen(options);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
case 'help': {
|
|
280
|
+
showUsage();
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
default: {
|
|
284
|
+
showUsage();
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
process.on('SIGTERM', shutdown);
|
|
291
|
+
process.on('SIGINT', shutdown);
|
|
292
|
+
|
|
293
|
+
async function shutdown() {
|
|
294
|
+
// close chrome instances launched by pair.js
|
|
295
|
+
await ChromeLauncher.killAll();
|
|
296
|
+
|
|
297
|
+
// close express server
|
|
298
|
+
if(server){
|
|
299
|
+
server.close()
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// destroy fcm client
|
|
303
|
+
if(fcmClient){
|
|
304
|
+
fcmClient.destroy();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
run();
|
package/cli/pair.html
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<html lang="en">
|
|
2
|
+
<head>
|
|
3
|
+
<title>RustPlus Pairing</title>
|
|
4
|
+
</head>
|
|
5
|
+
<body>
|
|
6
|
+
<div>To pair rustplus.js with Rust+, you must allow popup windows. You may need to refresh this page after enabling popups.</div>
|
|
7
|
+
<script type="text/javascript">
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Launch Rust+ login website in a popup window.
|
|
11
|
+
*/
|
|
12
|
+
var popupWindow = window.open("https://companion-rust.facepunch.com/login", "", "");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Each time the popup window changes origins (rust+ -> steam -> rust+), our changes to the window object are reset.
|
|
16
|
+
* So, every 250ms check if our handler is not registered, and register it if needed.
|
|
17
|
+
*/
|
|
18
|
+
var handlerInterval = setInterval(function() {
|
|
19
|
+
if(popupWindow.ReactNativeWebView === undefined){
|
|
20
|
+
console.log("registering ReactNativeWebView.postMessage handler");
|
|
21
|
+
popupWindow.ReactNativeWebView = {
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Rust+ login website calls ReactNativeWebView.postMessage after successfully logging in with Steam.
|
|
25
|
+
* @param message json string with SteamId and Token
|
|
26
|
+
*/
|
|
27
|
+
postMessage: function(message) {
|
|
28
|
+
|
|
29
|
+
// we no longer need the handler
|
|
30
|
+
clearInterval(handlerInterval);
|
|
31
|
+
|
|
32
|
+
// parse json message
|
|
33
|
+
var auth = JSON.parse(message);
|
|
34
|
+
|
|
35
|
+
// send the Token back to pair.js in the main window
|
|
36
|
+
window.location.href = "http://localhost:3000/callback?token=" + encodeURIComponent(auth.Token);
|
|
37
|
+
|
|
38
|
+
// close the popup window
|
|
39
|
+
popupWindow.close();
|
|
40
|
+
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}, 250);
|
|
46
|
+
|
|
47
|
+
</script>
|
|
48
|
+
</body>
|
|
49
|
+
</html>
|
package/docs/API.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Rust Companion API
|
|
2
|
+
|
|
3
|
+
Base URL
|
|
4
|
+
|
|
5
|
+
- https://companion-rust.facepunch.com
|
|
6
|
+
|
|
7
|
+
## App Endpoints
|
|
8
|
+
|
|
9
|
+
These endpoints are used by the Rust+ Companion App.
|
|
10
|
+
|
|
11
|
+
### `/api/avatar/{steamId}`
|
|
12
|
+
|
|
13
|
+
- Method: `GET`
|
|
14
|
+
|
|
15
|
+
Redirects to the Profile Image for the SteamId.
|
|
16
|
+
|
|
17
|
+
### `/api/history/read`
|
|
18
|
+
|
|
19
|
+
- Method: `POST`
|
|
20
|
+
- Headers:
|
|
21
|
+
- `Content-Type`: `application/json`
|
|
22
|
+
- Body: `AuthToken`
|
|
23
|
+
|
|
24
|
+
Response contains event history, such as Player Deaths and Server Pair Requests.
|
|
25
|
+
|
|
26
|
+
- Notification ID
|
|
27
|
+
- Notification Date
|
|
28
|
+
- Player SteamId
|
|
29
|
+
- Server ID
|
|
30
|
+
- Title (eg `{name} is online`)
|
|
31
|
+
- Body (server name)
|
|
32
|
+
- Data (event data)
|
|
33
|
+
|
|
34
|
+
### TODO
|
|
35
|
+
|
|
36
|
+
Endpoints that haven't been documented yet.
|
|
37
|
+
|
|
38
|
+
- POST /api/push/register
|
|
39
|
+
- DELETE /api/push/unregister
|
|
40
|
+
- POST /api/subscriptions/app/add
|
|
41
|
+
- POST /api/subscriptions/app/remove
|
|
42
|
+
- POST /api/subscriptions/app/check
|
|
43
|
+
- POST /api/channel/enable
|
|
44
|
+
- POST /api/channel/disable
|
|
45
|
+
- POST /api/channel/check
|
|
46
|
+
|
|
47
|
+
## Server Endpoints
|
|
48
|
+
|
|
49
|
+
These endpoints are used by the Rust Server.
|
|
50
|
+
|
|
51
|
+
### `/api/server/register`
|
|
52
|
+
|
|
53
|
+
- Method: `GET`
|
|
54
|
+
|
|
55
|
+
Registers a new Rust server with the Rust+ Companion API. This is called when the Rust server is initialized and has not registered, or refreshing the existing server token fails.
|
|
56
|
+
|
|
57
|
+
### `/api/server/refresh`
|
|
58
|
+
|
|
59
|
+
- Method: `POST`
|
|
60
|
+
- Headers:
|
|
61
|
+
- `Content-Type`: `text/plain`
|
|
62
|
+
- Body: `Existing Server Token`
|
|
63
|
+
|
|
64
|
+
Refreshes an existing server token.
|
|
65
|
+
|
|
66
|
+
### `/api/push/send`
|
|
67
|
+
|
|
68
|
+
- Method: `POST`
|
|
69
|
+
- Headers:
|
|
70
|
+
- `Content-Type`: `application/json`
|
|
71
|
+
- JSON Body:
|
|
72
|
+
- `ServerToken` (string)
|
|
73
|
+
- `SteamIds` (list of unsigned longs)
|
|
74
|
+
- `Channel` (int)
|
|
75
|
+
- `Pairing=1001`
|
|
76
|
+
- `PlayerLoggedIn=1002`
|
|
77
|
+
- `PlayerDied=1003`
|
|
78
|
+
- `SmartAlarm=1004`
|
|
79
|
+
- `Title` (string)
|
|
80
|
+
- `Body` (string)
|
|
81
|
+
- `Data` (dictionary<string, string>)
|
|
82
|
+
- `id` (string)
|
|
83
|
+
- `name` (string)
|
|
84
|
+
- `desc` (string)
|
|
85
|
+
- `img` (string)
|
|
86
|
+
- `logo` (string)
|
|
87
|
+
- `url` (string)
|
|
88
|
+
- `ip` (string)
|
|
89
|
+
- `port` (string)
|
|
90
|
+
- `playerId` (string)
|
|
91
|
+
- `playerToken` (string)
|
|
92
|
+
- `entityId` (string)
|
|
93
|
+
- `entityType` (string)
|
|
94
|
+
- `entityName` (string)
|
|
95
|
+
- `type` (string)
|
|
96
|
+
- `death`
|
|
97
|
+
- `login`
|
|
98
|
+
- `server`
|
|
99
|
+
- `entity`
|
|
100
|
+
|
|
101
|
+
Sends a push notification to the Rust+ Companion App.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Download Rust+ Bundle
|
|
2
|
+
|
|
3
|
+
You can use the following bash snippet to fetch the latest Rust+ bundle URL.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
curl -s -H 'Exponent-SDK-Version: 44.0.0' -H 'Exponent-Platform: android' -H 'Exponent-Version: 0.0.25' -H 'Accept: application/expo+json,application/json' -H 'Expo-Release-Channel: default' -H 'Expo-Api-Version: 1' -H 'Expo-Client-Environment: STANDALONE' -H 'Exponent-Accept-Signature: true' -H 'Expo-JSON-Error: true' -H 'Host: exp.host' -H 'User-Agent: okhttp/3.12.1' --compressed 'https://exp.host/@facepunch/RustCompanion' | python -c 'import sys, json; print(json.loads(json.load(sys.stdin)["manifestString"])["bundleUrl"])'
|
|
7
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Rust+ Pairing Flow
|
|
2
|
+
|
|
3
|
+
The procedure for pairing with a Rust Server and Smart Devices in the official Rust+ app is like so:
|
|
4
|
+
|
|
5
|
+
- Player installs Rust+ mobile app from Apple App Store or Google Play Store.
|
|
6
|
+
- Player logs in to Rust+ mobile app with Steam Account.
|
|
7
|
+
- Player gets redirected back to the Rust+ mobile app with a Steam OpenId Auth Token.
|
|
8
|
+
- Rust+ mobile app sends the `ExponentPushToken` ([expo app](https://expo.io/) notification token) for your device along with your Steam Auth Token to the `/api/push/register` endpoint of the Rust Companion API.
|
|
9
|
+
- Rust Companion API returns a refreshed Steam Auth Token which expires after 2 weeks.
|
|
10
|
+
- Player connects to Rust Server in game.
|
|
11
|
+
- Player goes to Rust+ menu in game.
|
|
12
|
+
- Player clicks "Pair with Server".
|
|
13
|
+
- Rust game server communicates with Rust Companion API with `serverToken` from `companion.id` file in Rust game server folder. (I haven't checked this, but assume that's how it works)
|
|
14
|
+
- Rust Companion API sends a notification to the registered device via the `ExponentPushToken`.
|
|
15
|
+
- The received Pairing Notification contains the Server Information required for the Rust+ app to connect to the WebSocket running on `app.port` configured in `server.cfg` while also authenticating as the Steam Account associated with the Steam OpenId token sent in previous steps.
|
|
16
|
+
- Player clicks "Pair" on "Smart Alarms" and "Smart Switches" in game.
|
|
17
|
+
- Rust game server communicates with Rust Companion API. (like above, I haven't checked this, but assume so)
|
|
18
|
+
- Rust Companion API sends another notification to the registered device.
|
|
19
|
+
- The received Pairing Notification contains the Entity Id and Entity Type (such as Smart Alarm or Smart Switch) as well as the Server Information you get from the normal Server Pairing process.
|
package/donate.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Donate
|
|
2
|
+
|
|
3
|
+
Thank you for considering donating, this helps support my work on this project 😁
|
|
4
|
+
|
|
5
|
+
## How can I donate?
|
|
6
|
+
|
|
7
|
+
- Bitcoin: bc1qy22smke8n4c54evdxmp7lpy9p0e6m9tavtlg2q
|
|
8
|
+
- Ethereum: 0xc64CFbA5D0BF7664158c5671F64d446395b3bF3D
|
|
9
|
+
- Buy me a Coffee: [https://ko-fi.com/liamcottle](https://ko-fi.com/liamcottle)
|
|
10
|
+
- Sponsor on GitHub: [https://github.com/sponsors/liamcottle](https://github.com/sponsors/liamcottle)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const RustPlus = require('../rustplus');
|
|
2
|
+
const Camera = require("../camera");
|
|
3
|
+
var rustplus = new RustPlus('ip', 'port', 'playerId', 'playerToken');
|
|
4
|
+
|
|
5
|
+
function delay(time) {
|
|
6
|
+
return new Promise(resolve => setTimeout(resolve, time));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
rustplus.on('connected', async () => {
|
|
10
|
+
|
|
11
|
+
console.log("connected");
|
|
12
|
+
|
|
13
|
+
// get a camera (should be an autoturret for this example)
|
|
14
|
+
const turret = rustplus.getCamera("TURRET1");
|
|
15
|
+
|
|
16
|
+
// wait until subscribed to autoturret camera
|
|
17
|
+
turret.on('subscribed', async () => {
|
|
18
|
+
|
|
19
|
+
// check if camera is an auto turret
|
|
20
|
+
if(!turret.isAutoTurret()){
|
|
21
|
+
console.log("Camera is not an auto turret!");
|
|
22
|
+
await rustplus.disconnect();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log("subscribed to autoturret camera");
|
|
27
|
+
await delay(500);
|
|
28
|
+
|
|
29
|
+
const shootCount = 3;
|
|
30
|
+
const shootDelay = 250;
|
|
31
|
+
const moveDelay = 500;
|
|
32
|
+
const moveAmount = 5;
|
|
33
|
+
|
|
34
|
+
// shoot autoturret
|
|
35
|
+
console.log("shooting");
|
|
36
|
+
for(let i = 0; i < shootCount; i++){
|
|
37
|
+
await delay(shootDelay);
|
|
38
|
+
await turret.shoot();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// move autoturret left
|
|
42
|
+
console.log("moving left");
|
|
43
|
+
await delay(moveDelay);
|
|
44
|
+
await turret.move(Camera.Buttons.NONE, -moveAmount, 0);
|
|
45
|
+
|
|
46
|
+
// shoot autoturret
|
|
47
|
+
console.log("shooting");
|
|
48
|
+
for(let i = 0; i < shootCount; i++){
|
|
49
|
+
await delay(shootDelay);
|
|
50
|
+
await turret.shoot();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// move autoturret up
|
|
54
|
+
console.log("moving up");
|
|
55
|
+
await delay(moveDelay);
|
|
56
|
+
await turret.move(Camera.Buttons.NONE, 0, moveAmount);
|
|
57
|
+
|
|
58
|
+
// shoot autoturret
|
|
59
|
+
console.log("shooting");
|
|
60
|
+
for(let i = 0; i < shootCount; i++){
|
|
61
|
+
await delay(shootDelay);
|
|
62
|
+
await turret.shoot();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// move autoturret right
|
|
66
|
+
console.log("moving right");
|
|
67
|
+
await delay(moveDelay);
|
|
68
|
+
await turret.move(Camera.Buttons.NONE, moveAmount, 0);
|
|
69
|
+
|
|
70
|
+
// shoot autoturret
|
|
71
|
+
console.log("shooting");
|
|
72
|
+
for(let i = 0; i < shootCount; i++){
|
|
73
|
+
await delay(shootDelay);
|
|
74
|
+
await turret.shoot();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// move autoturret down
|
|
78
|
+
console.log("moving down");
|
|
79
|
+
await delay(moveDelay);
|
|
80
|
+
await turret.move(Camera.Buttons.NONE, 0, -moveAmount);
|
|
81
|
+
|
|
82
|
+
// shoot autoturret
|
|
83
|
+
console.log("shooting");
|
|
84
|
+
for(let i = 0; i < shootCount; i++){
|
|
85
|
+
await delay(shootDelay);
|
|
86
|
+
await turret.shoot();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log("disconnecting");
|
|
90
|
+
await rustplus.disconnect();
|
|
91
|
+
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// subscribe to autoturret camera
|
|
95
|
+
await turret.subscribe();
|
|
96
|
+
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// connect to rust server
|
|
100
|
+
rustplus.connect();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const RustPlus = require('@rustwirebot/rustplus.js');
|
|
2
|
+
var rustplus = new RustPlus('ip', 'port', 'playerId', 'playerToken');
|
|
3
|
+
|
|
4
|
+
// wait until connected before sending commands
|
|
5
|
+
rustplus.on('connected', () => {
|
|
6
|
+
|
|
7
|
+
// send message to team chat
|
|
8
|
+
rustplus.sendTeamMessage('Hello from rustplus.js!');
|
|
9
|
+
|
|
10
|
+
// disconnect from rust server
|
|
11
|
+
rustplus.disconnect();
|
|
12
|
+
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// connect to rust server
|
|
16
|
+
rustplus.connect();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const RustPlus = require('@rustwirebot/rustplus.js');
|
|
2
|
+
var rustplus = new RustPlus('ip', 'port', 'playerId', 'playerToken');
|
|
3
|
+
|
|
4
|
+
// wait until connected before sending commands
|
|
5
|
+
rustplus.on('connected', () => {
|
|
6
|
+
|
|
7
|
+
// turn smart switch on by entity id
|
|
8
|
+
rustplus.turnSmartSwitchOn(1234567, (message) => {
|
|
9
|
+
|
|
10
|
+
// log result
|
|
11
|
+
console.log("turnSmartSwitchOn result: " + JSON.stringify(message));
|
|
12
|
+
|
|
13
|
+
// disconnect from rust server
|
|
14
|
+
rustplus.disconnect();
|
|
15
|
+
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// connect to rust server
|
|
21
|
+
rustplus.connect();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const RustPlus = require('@rustwirebot/rustplus.js');
|
|
2
|
+
var rustplus = new RustPlus('ip', 'port', 'playerId', 'playerToken');
|
|
3
|
+
|
|
4
|
+
// wait until connected before sending commands
|
|
5
|
+
rustplus.on('connected', () => {
|
|
6
|
+
|
|
7
|
+
// turn smart switch off by entity id
|
|
8
|
+
rustplus.turnSmartSwitchOff(1234567, (message) => {
|
|
9
|
+
|
|
10
|
+
// log result
|
|
11
|
+
console.log("turnSmartSwitchOff result: " + JSON.stringify(message));
|
|
12
|
+
|
|
13
|
+
// disconnect from rust server
|
|
14
|
+
rustplus.disconnect();
|
|
15
|
+
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// connect to rust server
|
|
21
|
+
rustplus.connect();
|