playactor
Version:
play around with your playstation devices
357 lines (356 loc) • 14.5 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeviceOptions = exports.DiscoveryOptions = exports.InputOutputOptions = void 0;
const debug_1 = __importDefault(require("debug"));
const clime_1 = require("clime");
const readline_1 = __importDefault(require("readline"));
const credentials_1 = require("../credentials");
const pending_1 = require("../device/pending");
const model_1 = require("../discovery/model");
const standard_1 = require("../discovery/standard");
const mim_requester_1 = require("../credentials/mim-requester");
const disk_storage_1 = require("../credentials/disk-storage");
const root_managing_1 = require("../credentials/root-managing");
const cli_proxy_1 = require("./cli-proxy");
const root_proxy_device_1 = require("./root-proxy-device");
const pin_accepting_device_1 = require("./pin-accepting-device");
const rejecting_requester_1 = require("../credentials/rejecting-requester");
const pass_code_1 = require("./pass-code");
const model_2 = require("../socket/model");
const cli_1 = require("../credentials/oauth/cli");
const requester_1 = require("../credentials/oauth/requester");
const device_type_strategy_1 = require("../credentials/device-type-strategy");
const write_only_storage_1 = require("../credentials/write-only-storage");
const log = debug_1.default("playactor:cli:options");
class InputOutputOptions extends clime_1.Options {
constructor() {
/* eslint-disable no-console */
super(...arguments);
this.enableDebug = false;
this.machineFriendly = false;
this.dontAutoOpenUrls = false;
}
logError(error) {
console.error(error);
}
logInfo(message) {
// NOTE: log on stderr by default so only "results"
// are on stdout.
console.error(message);
}
logResult(result) {
if (typeof result === "string") {
console.log(result);
}
else if (this.machineFriendly) {
console.log(JSON.stringify(result));
}
else {
console.log(JSON.stringify(result, null, 2));
}
}
prompt(promptText) {
return new Promise(resolve => {
const prompter = readline_1.default.createInterface({
input: process.stdin,
output: process.stdout,
});
prompter.question(promptText, result => {
prompter.close();
resolve(result);
});
});
}
configureLogging() {
return __awaiter(this, void 0, void 0, function* () {
if (this.enableDebug) {
debug_1.default.enable("playactor:*");
}
});
}
}
__decorate([
clime_1.option({
name: "debug",
description: "Enable debug logging",
toggle: true,
}),
__metadata("design:type", Object)
], InputOutputOptions.prototype, "enableDebug", void 0);
__decorate([
clime_1.option({
name: "machine-friendly",
description: "Enable machine-friendly output",
toggle: true,
}),
__metadata("design:type", Object)
], InputOutputOptions.prototype, "machineFriendly", void 0);
__decorate([
clime_1.option({
name: "no-open-urls",
description: "Disable URL-opening convenience",
toggle: true,
}),
__metadata("design:type", Object)
], InputOutputOptions.prototype, "dontAutoOpenUrls", void 0);
exports.InputOutputOptions = InputOutputOptions;
class DiscoveryOptions extends InputOutputOptions {
constructor() {
super(...arguments);
this.searchTimeout = model_1.defaultDiscoveryConfig.timeoutMillis;
this.connectTimeout = model_2.defaultSocketConfig.connectTimeoutMillis;
}
get discoveryConfig() {
return {
timeoutMillis: this.searchTimeout,
};
}
get networkConfig() {
return {
localBindAddress: this.localBindAddress,
localBindPort: this.localBindPort,
};
}
get socketConfig() {
return Object.assign(Object.assign({}, model_2.defaultSocketConfig), { connectTimeoutMillis: this.connectTimeout });
}
}
__decorate([
clime_1.option({
name: "timeout",
default: model_1.defaultDiscoveryConfig.timeoutMillis,
description: "How long to look for device(s)",
placeholder: "millis",
}),
__metadata("design:type", Number)
], DiscoveryOptions.prototype, "searchTimeout", void 0);
__decorate([
clime_1.option({
name: "connect-timeout",
default: model_2.defaultSocketConfig.connectTimeoutMillis,
description: "How long to look wait for connection",
placeholder: "millis",
}),
__metadata("design:type", Number)
], DiscoveryOptions.prototype, "connectTimeout", void 0);
__decorate([
clime_1.option({
name: "bind-address",
description: "Bind to a specific network adapter IP",
placeholder: "ip",
}),
__metadata("design:type", String)
], DiscoveryOptions.prototype, "localBindAddress", void 0);
__decorate([
clime_1.option({
name: "bind-port",
description: "Bind on a specific port",
placeholder: "port",
}),
__metadata("design:type", Number)
], DiscoveryOptions.prototype, "localBindPort", void 0);
exports.DiscoveryOptions = DiscoveryOptions;
class DeviceOptions extends DiscoveryOptions {
constructor() {
super(...arguments);
this.dontAuthenticate = false;
this.alwaysAuthenticate = false;
this.deviceOnlyPS4 = false;
this.deviceOnlyPS5 = false;
}
findDevice() {
var _a;
return __awaiter(this, void 0, void 0, function* () {
this.configureLogging();
log("findDevice with:", this);
const { description, predicate } = this.configurePending();
const networkConfig = Object.assign({}, this.connectionConfig.network);
const args = process.argv;
const proxiedUserId = root_proxy_device_1.RootProxyDevice.extractProxiedUserId(args);
const { requestedDeviceType: deviceType, networkFactory } = this;
const diskCredentialsStorage = new disk_storage_1.DiskCredentialsStorage(this.credentialsPath);
const credentialsStorage = this.alwaysAuthenticate
? new write_only_storage_1.WriteOnlyStorage(diskCredentialsStorage)
: diskCredentialsStorage;
const credentialsRequester = this.dontAuthenticate
? new rejecting_requester_1.RejectingCredentialRequester("Not authenticated")
: this.buildCredentialsRequester(networkFactory, networkConfig, proxiedUserId);
const credentials = new credentials_1.CredentialManager(credentialsRequester, credentialsStorage);
const device = new pending_1.PendingDevice(description, predicate, networkConfig, Object.assign(Object.assign({}, this.discoveryConfig), { deviceIp: this.deviceIp, deviceType }), networkFactory, credentials);
yield device.discover();
if (this.dontAuthenticate) {
// no sense doing extra work
return device;
}
// if we got here, the device was found! wrap it up in case we
// need we need to request root privileges or something to
// complete the login process
return new root_proxy_device_1.RootProxyDevice(this, new cli_proxy_1.SudoCliProxy(), new pin_accepting_device_1.PinAcceptingDevice(this, device), {
providedCredentialsPath: this.credentialsPath,
effectiveCredentialsPath: diskCredentialsStorage.filePath,
invocationArgs: args,
currentUserId: (_a = process.getuid) === null || _a === void 0 ? void 0 : _a.call(process),
});
});
}
get connectionConfig() {
var _a, _b;
return {
network: this.networkConfig,
socket: this.socketConfig,
login: {
passCode: (_b = (_a = this.passCode) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "",
},
};
}
get networkFactory() {
const deviceType = this.requestedDeviceType;
if (deviceType === model_1.DeviceType.PS4) {
return standard_1.StandardPS4DiscoveryNetworkFactory;
}
if (deviceType === model_1.DeviceType.PS5) {
return standard_1.StandardPS5DiscoveryNetworkFactory;
}
return standard_1.StandardDiscoveryNetworkFactory;
}
get requestedDeviceType() {
if (this.deviceOnlyPS4 && this.deviceOnlyPS5) {
const flags = ["--ps4", "--ps5"].join(", ");
throw new clime_1.ExpectedError(
// eslint-disable-next-line prefer-template
`You must have no more than one of any of these flags:\n ${flags}`);
}
else if (this.deviceOnlyPS4) {
return model_1.DeviceType.PS4;
}
else if (this.deviceOnlyPS5) {
return model_1.DeviceType.PS5;
}
}
buildCredentialsRequester(networkFactory, networkConfig, proxiedUserId) {
const ps4 = new root_managing_1.RootManagingCredentialRequester(new mim_requester_1.MimCredentialRequester(networkFactory, networkConfig, this), proxiedUserId);
const ps5 = new requester_1.OauthCredentialRequester(this, new cli_1.CliOauthStrategy(this, !this.dontAutoOpenUrls));
return new device_type_strategy_1.DeviceTypeStrategyCredentialRequester({
[model_1.DeviceType.PS4]: ps4,
[model_1.DeviceType.PS5]: ps5,
});
}
configurePending() {
let description = "device (any)";
let predicate = () => true;
if (this.deviceIp) {
description = `device at ${this.deviceIp}`;
predicate = device => device.address.address === this.deviceIp;
}
else if (this.deviceHostName) {
description = `device named ${this.deviceHostName}`;
predicate = device => device.name === this.deviceHostName;
}
else if (this.deviceHostId) {
description = `device with id ${this.deviceHostId}`;
predicate = device => device.id === this.deviceHostId;
}
const requestedType = this.requestedDeviceType;
if (requestedType) {
const base = predicate;
description = `${requestedType} ${description}`;
predicate = device => base(device)
&& device.type === requestedType;
}
return { description, predicate };
}
}
__decorate([
clime_1.option({
name: "no-auth",
description: "Don't attempt to authenticate if not already",
toggle: true,
}),
__metadata("design:type", Object)
], DeviceOptions.prototype, "dontAuthenticate", void 0);
__decorate([
clime_1.option({
name: "force-auth",
description: "Ignore existing credentials",
toggle: true,
}),
__metadata("design:type", Object)
], DeviceOptions.prototype, "alwaysAuthenticate", void 0);
__decorate([
clime_1.option({
name: "credentials",
flag: "c",
placeholder: "path",
description: "Path to a file for storing credentials",
}),
__metadata("design:type", String)
], DeviceOptions.prototype, "credentialsPath", void 0);
__decorate([
clime_1.option({
name: "pass-code",
flag: "p",
description: "Your numeric passcode, or a string of key names",
}),
__metadata("design:type", pass_code_1.CliPassCode)
], DeviceOptions.prototype, "passCode", void 0);
__decorate([
clime_1.option({
name: "ip",
description: "Select a specific device by IP",
}),
__metadata("design:type", String)
], DeviceOptions.prototype, "deviceIp", void 0);
__decorate([
clime_1.option({
name: "host-name",
description: "Select a specific device by its host-name",
placeholder: "name",
}),
__metadata("design:type", String)
], DeviceOptions.prototype, "deviceHostName", void 0);
__decorate([
clime_1.option({
name: "host-id",
description: "Select a specific device by its host-id",
placeholder: "id",
}),
__metadata("design:type", String)
], DeviceOptions.prototype, "deviceHostId", void 0);
__decorate([
clime_1.option({
name: "ps4",
description: "Ignore non-PS4 devices",
toggle: true,
}),
__metadata("design:type", Object)
], DeviceOptions.prototype, "deviceOnlyPS4", void 0);
__decorate([
clime_1.option({
name: "ps5",
description: "Ignore non-PS5 devices",
toggle: true,
}),
__metadata("design:type", Object)
], DeviceOptions.prototype, "deviceOnlyPS5", void 0);
exports.DeviceOptions = DeviceOptions;