Chat service implemented.

This commit is contained in:
Jared Goodwin 2020-01-25 01:19:21 -08:00
parent e604f22d53
commit df7de3681f
20 changed files with 295 additions and 65 deletions

View File

@ -46,6 +46,7 @@ namespace Remotely.Agent
builder.AddConsole().AddEventLog();
});
serviceCollection.AddSingleton<DeviceSocket>();
serviceCollection.AddScoped<ChatClientService>();
serviceCollection.AddScoped<Bash>();
serviceCollection.AddScoped<CMD>();
serviceCollection.AddScoped<PSCore>();

View File

@ -21,6 +21,48 @@ namespace Remotely.Agent.Services
private ConnectionInfo ConnectionInfo { get; }
public async Task LaunchChatService(string requesterID, HubConnection hubConnection)
{
try
{
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName);
if (!File.Exists(rcBinaryPath))
{
await hubConnection.InvokeAsync("DisplayMessage", "Chat executable not found on target device.", "Executable not found on device.", requesterID);
}
// Start ScreenCast.
await hubConnection.InvokeAsync("DisplayMessage", $"Starting chat service...", "Starting chat service.", requesterID);
if (OSUtils.IsWindows)
{
if (Program.IsDebug)
{
Process.Start("conhost.exe", $"{rcBinaryPath} -mode Chat -requester {requesterID}");
}
else
{
var result = Win32Interop.OpenInteractiveProcess($"conhost.exe {rcBinaryPath} -mode Chat -requester {requesterID}", "default", false, out _);
if (!result)
{
await hubConnection.InvokeAsync("DisplayMessage", "Remote control failed to start on target device.", "Failed to start remote control.", requesterID);
}
}
}
else if (OSUtils.IsLinux)
{
var args = $"bash -c {rcBinaryPath} -mode Chat -requester {requesterID} & disown";
StartLinuxScreenCaster(args);
}
}
catch (Exception ex)
{
Logger.Write(ex);
await hubConnection.InvokeAsync("DisplayMessage", "Remote control failed to start on target device.", "Failed to start remote control.", requesterID);
}
}
public async Task LaunchRemoteControl(string requesterID, string serviceID, HubConnection hubConnection)
{
try
@ -54,17 +96,15 @@ namespace Remotely.Agent.Services
else if (OSUtils.IsLinux)
{
var args = $"{rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConnectionInfo.Host} & disown";
await StartLinuxScreenCaster(args);
StartLinuxScreenCaster(args);
}
}
catch (Exception ex)
{
Logger.Write(ex);
await hubConnection.InvokeAsync("DisplayMessage", "Remote control failed to start on target device.", "Failed to start remote control.", requesterID);
throw;
}
}
public async Task RestartScreenCaster(List<string> viewerIDs, string serviceID, string requesterID, HubConnection hubConnection)
{
try
@ -104,7 +144,7 @@ namespace Remotely.Agent.Services
else if (OSUtils.IsLinux)
{
var args = $"{rcBinaryPath} -mode Unattended -requester {requesterID} -serviceid {serviceID} -deviceid {ConnectionInfo.DeviceID} -host {ConnectionInfo.Host} -relaunch true -viewers {string.Join(",", viewerIDs)} & disown";
await StartLinuxScreenCaster(args);
StartLinuxScreenCaster(args);
}
}
catch (Exception ex)
@ -115,7 +155,7 @@ namespace Remotely.Agent.Services
}
}
private async Task StartLinuxScreenCaster(string args)
private void StartLinuxScreenCaster(string args)
{
var xauthority = OSUtils.StartProcessWithResults("find", $"/ -name Xauthority").Split('\n', StringSplitOptions.RemoveEmptyEntries).First();
var display = ":0";
@ -140,8 +180,7 @@ namespace Remotely.Agent.Services
psi.Environment.Add("DISPLAY", display);
psi.Environment.Add("XAUTHORITY", xauthority);
Logger.Write($"Attempting to launch screen caster with username {username}, xauthority {xauthority}, and display {display}.");
var casterProc = Process.Start(psi);
await Task.Run(() => { casterProc.WaitForExit(); });
Process.Start(psi);
}
}
}

View File

@ -0,0 +1,97 @@
using Microsoft.AspNetCore.SignalR.Client;
using Remotely.Shared.Services;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Remotely.Agent.Services
{
public class ChatClientService
{
public ChatClientService(AppLauncher appLauncher)
{
AppLauncher = appLauncher;
}
private SemaphoreSlim MessageLock { get; } = new SemaphoreSlim(1);
private AppLauncher AppLauncher { get; }
private CacheItemPolicy CacheItemPolicy { get; } = new CacheItemPolicy()
{
SlidingExpiration = TimeSpan.FromMinutes(10),
RemovedCallback = new CacheEntryRemovedCallback(args =>
{
(args.CacheItem.Value as NamedPipeClientStream).Dispose();
})
};
private MemoryCache ChatClients { get; } = new MemoryCache("ChatClients");
public async Task SendMessage(string message, string senderConnectionID, HubConnection hubConnection)
{
try
{
if (await MessageLock.WaitAsync(30000))
{
NamedPipeClientStream clientPipe;
if (!ChatClients.Contains(senderConnectionID))
{
var rcBinaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ScreenCast", OSUtils.ScreenCastExecutableFileName);
await AppLauncher.LaunchChatService(senderConnectionID, hubConnection);
clientPipe = new NamedPipeClientStream(".", "Remotely_Chat" + senderConnectionID, PipeDirection.InOut, PipeOptions.Asynchronous);
clientPipe.Connect(15000);
if (!clientPipe.IsConnected)
{
Logger.Write("Failed to connect to chat host.");
return;
}
ChatClients.Add(senderConnectionID, clientPipe, CacheItemPolicy);
}
clientPipe = (NamedPipeClientStream)ChatClients.Get(senderConnectionID);
if (!clientPipe.IsConnected)
{
ChatClients.Remove(senderConnectionID);
await hubConnection.SendAsync("DisplayMessage", "Chat disconnected. Please try again.", "Chat disconnected.");
return;
}
using (var sw = new StreamWriter(clientPipe, leaveOpen: true))
{
await sw.WriteLineAsync(message);
await sw.FlushAsync();
}
_ = Task.Run(async () => { await ReadFromStream(clientPipe, senderConnectionID, hubConnection); });
}
}
catch (Exception ex)
{
Logger.Write(ex);
}
finally
{
MessageLock.Release();
}
}
private async Task ReadFromStream(NamedPipeClientStream clientPipe, string senderConnectionID, HubConnection hubConnection)
{
while (clientPipe.IsConnected)
{
using (var sr = new StreamReader(clientPipe, leaveOpen: true))
{
var message = await sr.ReadLineAsync();
await hubConnection.SendAsync("Chat", message, senderConnectionID);
}
}
}
}
}

View File

@ -24,7 +24,8 @@ namespace Remotely.Agent.Services
Uninstaller uninstaller,
CommandExecutor commandExecutor,
ScriptRunner scriptRunner,
AppLauncher appLauncher)
AppLauncher appLauncher,
ChatClientService chatService)
{
Updater = updater;
ConfigService = configService;
@ -32,9 +33,11 @@ namespace Remotely.Agent.Services
CommandExecutor = commandExecutor;
ScriptRunner = scriptRunner;
AppLauncher = appLauncher;
ChatService = chatService;
}
public bool IsConnected => HubConnection?.State == HubConnectionState.Connected;
private AppLauncher AppLauncher { get; }
private ChatClientService ChatService { get; }
private CommandExecutor CommandExecutor { get; }
private ConfigService ConfigService { get; }
private ConnectionInfo ConnectionInfo { get; set; }
@ -53,7 +56,7 @@ namespace Remotely.Agent.Services
.AddMessagePackProtocol()
.Build();
RegisterMessageHandlers(HubConnection);
RegisterMessageHandlers();
await HubConnection.StartAsync();
@ -78,21 +81,24 @@ namespace Remotely.Agent.Services
HeartbeatTimer.Elapsed += HeartbeatTimer_Elapsed;
HeartbeatTimer.Start();
}
public void SendHeartbeat()
{
var currentInfo = Device.Create(ConnectionInfo);
HubConnection.InvokeAsync("DeviceHeartbeat", currentInfo);
}
private void HeartbeatTimer_Elapsed(object sender, ElapsedEventArgs e)
{
SendHeartbeat();
}
private void RegisterMessageHandlers(HubConnection hubConnection)
private void RegisterMessageHandlers()
{
hubConnection.On("ExecuteCommand", (async (string mode, string command, string commandID, string senderConnectionID) =>
HubConnection.On("Chat", async (string message, string senderConnectionID) => {
await ChatService.SendMessage(message, senderConnectionID, HubConnection);
});
HubConnection.On("ExecuteCommand", (async (string mode, string command, string commandID, string senderConnectionID) =>
{
if (!IsServerVerified)
{
@ -101,9 +107,9 @@ namespace Remotely.Agent.Services
return;
}
await CommandExecutor.ExecuteCommand(mode, command, commandID, senderConnectionID, hubConnection);
await CommandExecutor.ExecuteCommand(mode, command, commandID, senderConnectionID, HubConnection);
}));
hubConnection.On("TransferFiles", async (string transferID, List<string> fileIDs, string requesterID) =>
HubConnection.On("TransferFiles", async (string transferID, List<string> fileIDs, string requesterID) =>
{
Logger.Write($"File transfer started by {requesterID}.");
var sharedFilePath = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(),"RemotelySharedFiles")).FullName;
@ -132,9 +138,9 @@ namespace Remotely.Agent.Services
}
}
}
await HubConnection.InvokeAsync("TransferCompleted", transferID, requesterID);
await this.HubConnection.InvokeAsync("TransferCompleted", transferID, requesterID);
});
hubConnection.On("DeployScript", async (string mode, string fileID, string commandContextID, string requesterID) => {
HubConnection.On("DeployScript", async (string mode, string fileID, string commandContextID, string requesterID) => {
if (!IsServerVerified)
{
Logger.Write($"Script deploy attempted before server was verified. Mode: {mode}. File ID: {fileID}. Sender: {requesterID}");
@ -142,14 +148,14 @@ namespace Remotely.Agent.Services
return;
}
await ScriptRunner.RunScript(mode, fileID, commandContextID, requesterID, hubConnection);
await ScriptRunner.RunScript(mode, fileID, commandContextID, requesterID, HubConnection);
});
hubConnection.On("UninstallClient", () =>
HubConnection.On("UninstallClient", () =>
{
Uninstaller.UninstallAgent();
});
hubConnection.On("RemoteControl", async (string requesterID, string serviceID) =>
HubConnection.On("RemoteControl", async (string requesterID, string serviceID) =>
{
if (!IsServerVerified)
{
@ -157,9 +163,9 @@ namespace Remotely.Agent.Services
Uninstaller.UninstallAgent();
return;
}
await AppLauncher.LaunchRemoteControl(requesterID, serviceID, hubConnection);
await AppLauncher.LaunchRemoteControl(requesterID, serviceID, HubConnection);
});
hubConnection.On("RestartScreenCaster", async (List<string> viewerIDs, string serviceID, string requesterID) =>
HubConnection.On("RestartScreenCaster", async (List<string> viewerIDs, string serviceID, string requesterID) =>
{
if (!IsServerVerified)
{
@ -167,14 +173,14 @@ namespace Remotely.Agent.Services
Uninstaller.UninstallAgent();
return;
}
await AppLauncher.RestartScreenCaster(viewerIDs, serviceID, requesterID, hubConnection);
await AppLauncher.RestartScreenCaster(viewerIDs, serviceID, requesterID, HubConnection);
});
hubConnection.On("CtrlAltDel", () =>
HubConnection.On("CtrlAltDel", () =>
{
User32.SendSAS(false);
});
hubConnection.On("ServerVerificationToken", (string verificationToken) =>
HubConnection.On("ServerVerificationToken", (string verificationToken) =>
{
if (verificationToken == ConnectionInfo.ServerVerificationToken)
{

View File

@ -68,9 +68,13 @@ namespace Remotely.ScreenCast.Core
Host = ArgDict["host"];
}
if (Mode == AppMode.Unattended)
if (Mode == AppMode.Chat || Mode == AppMode.Unattended)
{
RequesterID = ArgDict["requester"];
}
if (Mode == AppMode.Unattended)
{
ServiceID = ArgDict["serviceid"];
DeviceID = ArgDict["deviceid"];
}

View File

@ -2,17 +2,18 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Remotely.ScreenCast.Core.Services
{
public class ChatService
public class ChatHostService
{
private NamedPipeServerStream NamedPipeStream { get; set; }
private string AsciiLogo => @"
_____ _ _
| __ \ | | | |
@ -24,9 +25,16 @@ namespace Remotely.ScreenCast.Core.Services
|___/
";
public async Task StartChat()
private NamedPipeServerStream NamedPipeStream { get; set; }
private StreamWriter Writer { get; set; }
private StreamReader Reader { get; set; }
public async Task StartChat(string requesterID)
{
NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + Process.GetCurrentProcess().Id, PipeDirection.InOut, 1);
NamedPipeStream = new NamedPipeServerStream("Remotely_Chat" + requesterID, PipeDirection.InOut, 10, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
Writer = new StreamWriter(NamedPipeStream);
Reader = new StreamReader(NamedPipeStream);
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Title = "Remotely Chat";
@ -43,27 +51,53 @@ namespace Remotely.ScreenCast.Core.Services
await Task.Delay(10000);
if (!NamedPipeStream.IsConnected)
{
Console.WriteLine("Connection failed. Closing...");
await Task.Delay(3000);
Environment.Exit(0);
await Close();
}
});
var cts = new CancellationTokenSource(10000);
await NamedPipeStream.WaitForConnectionAsync(cts.Token);
_ = Task.Run(ReadFromStream);
Console.WriteLine("You're now connected with a technician.");
Console.WriteLine();
Console.WriteLine("Type your responses below and hit Enter to send.");
Console.WriteLine("Press Ctrl + C to exit.");
Console.WriteLine();
while (true)
while (NamedPipeStream.IsConnected)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("You: ");
Console.ForegroundColor = ConsoleColor.White;
var input = Console.ReadLine();
await NamedPipeStream.WriteAsync(Encoding.UTF8.GetBytes(input));
SetPrompt();
var message = Console.ReadLine();
await Writer.WriteLineAsync(message);
await Writer.FlushAsync();
}
await Close();
}
private async Task Close()
{
Console.WriteLine("Connection failed. Closing...");
await Task.Delay(3000);
Environment.Exit(0);
}
private async Task ReadFromStream()
{
while (NamedPipeStream.IsConnected)
{
var message = await Reader.ReadLineAsync();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine(message);
SetPrompt();
}
}
private void SetPrompt()
{
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("You: ");
Console.ForegroundColor = ConsoleColor.White;
}
}
}

View File

@ -45,7 +45,7 @@ namespace Remotely.ScreenCast.Win
if (Conductor.Mode == Core.Enums.AppMode.Chat)
{
Services.GetRequiredService<ChatService>().StartChat().Wait();
Services.GetRequiredService<ChatHostService>().StartChat(Conductor.RequesterID).Wait();
}
else
{
@ -76,7 +76,7 @@ namespace Remotely.ScreenCast.Win
serviceCollection.AddSingleton<CasterSocket>();
serviceCollection.AddSingleton<IdleTimer>();
serviceCollection.AddSingleton<Conductor>();
serviceCollection.AddSingleton<ChatService>();
serviceCollection.AddSingleton<ChatHostService>();
serviceCollection.AddTransient<ICapturer>(provider =>
{
try

View File

@ -3,7 +3,8 @@
@{
ViewData["Title"] = "Invite";
}
<h2>Organization Invite</h2>
<h3>Organization Invite</h3>
<hr />
<div class="row">
<div class="col-sm-12">
@if (Model.Success)
@ -16,9 +17,19 @@
else
{
<h5>Uh oh.</h5>
<p>@Model.Message</p>
<form method="post">
<div>
<strong class="text-danger">WARNING: </strong>
<p class="text-danger">You will leave your current organization and lose access to its agents unless someone is able to invite you back in.</p>
<p>Are you sure you want to leave your current organization and join this one?</p>
</div>
<div asp-validation-summary="All" class="text-danger"></div>
<input asp-for="Input.InviteID" hidden />
<div class="form-group">
<button type="button" class="btn btn-secondary mr-3" onclick="location.assign(location.origin)">Cancel</button>
<button type="submit" class="btn btn-danger">Confirm</button>
</div>
</form>
}
</div>

View File

@ -18,27 +18,43 @@ namespace Remotely.Server.Pages
}
private DataService DataService { get; }
public bool Success { get; set; }
public string Message { get; set; }
public class InputModel
{
public string InviteID { get; set; }
}
[BindProperty]
public InputModel Input { get; set; } = new InputModel();
public void OnGet(string id)
{
if (string.IsNullOrWhiteSpace(id))
{
Success = false;
Message = "No invititation ID is specified.";
return;
ModelState.AddModelError("MissingID", "No invititation ID is specified.");
}
var result = DataService.JoinViaInvitation(User.Identity.Name, id);
Input.InviteID = id;
}
public IActionResult OnPost()
{
if (string.IsNullOrWhiteSpace(Input?.InviteID))
{
Success = false;
ModelState.AddModelError("MissingID", "No invititation ID is specified.");
return Page();
}
var result = DataService.JoinViaInvitation(User.Identity.Name, Input.InviteID);
if (result == false)
{
Success = false;
Message = "The invitation ID wasn't found or is for another account.";
return;
ModelState.AddModelError("InviteIDNotFound", "The invitation ID wasn't found or is for another account.");
}
Success = true;
Message = "You've successfully joined the organization.";
}
Success = true;
return Page();
}
}
}

View File

@ -3,7 +3,6 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<UserSecretsId>aspnet-DoXM_Server-C4DA754F-DF49-46C8-9091-A7EC11A0D311</UserSecretsId>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<DockerTargetOS>Windows</DockerTargetOS>
<AssemblyVersion>1.0.0.0</AssemblyVersion>

View File

@ -49,6 +49,13 @@ namespace Remotely.Server.Services
}
private SignInManager<RemotelyUser> SignInManager { get; }
public async Task Chat(string message, string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);
var connections = GetActiveClientConnections(deviceIDs);
await DeviceHub.Clients.Clients(connections.Select(x => x.Key).ToList()).SendAsync("Chat", $"{RemotelyUser.UserName}: {message}", Context.ConnectionId);
}
public async Task DeployScript(string fileID, string mode, string[] deviceIDs)
{
deviceIDs = DataService.FilterDeviceIDsByUserPermission(deviceIDs, RemotelyUser);

View File

@ -39,13 +39,17 @@ namespace Remotely.Server.Services
}
}
public async void BashResultViaAjax(string commandID)
public async Task BashResultViaAjax(string commandID)
{
var commandContext = DataService.GetCommandContext(commandID);
await BrowserHub.Clients.Client(commandContext.SenderConnectionID).SendAsync("BashResultViaAjax", commandID, Device.ID);
}
public async Task Chat(string message, string senderConnectionID)
{
await BrowserHub.Clients.Client(senderConnectionID).SendAsync("Chat", Device.DeviceName, message);
}
public async void CMDResultViaAjax(string commandID)
public async Task CMDResultViaAjax(string commandID)
{
var commandContext = DataService.GetCommandContext(commandID);
await BrowserHub.Clients.Client(commandContext.SenderConnectionID).SendAsync("CMDResultViaAjax", commandID, Device.ID);

View File

@ -149,7 +149,9 @@ function Install-DesktopRuntime() {
Write-Host ".NET Core Windows Desktop runtime not found. Downloading installer."
$Response = Invoke-WebRequest -Uri "https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.1-windows-x64-installer" -UseBasicParsing
$DownloadLink = $Response.Links | Where-Object { $_.href -like "*windowsdesktop-runtime*" }
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri $DownloadLink.href -OutFile "$env:TEMP\windowsdesktop-runtime.exe"
$ProgressPreference = 'Continue'
Write-Host "Installing .NET Core Windows Desktop runtime."
Start-Process -FilePath "$env:TEMP\windowsdesktop-runtime.exe" -ArgumentList "/install /quiet /norestart" -Wait
}

View File

@ -142,7 +142,9 @@ function Install-DesktopRuntime() {
Write-Host ".NET Core Windows Desktop runtime not found. Downloading installer."
$Response = Invoke-WebRequest -Uri "https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.1-windows-x86-installer" -UseBasicParsing
$DownloadLink = $Response.Links | Where-Object { $_.href -like "*windowsdesktop-runtime*" }
$ProgressPreference = 'SilentlyContinue'
Invoke-WebRequest -Uri $DownloadLink.href -OutFile "$env:TEMP\windowsdesktop-runtime.exe"
$ProgressPreference = 'Continue'
Write-Host "Installing .NET Core Windows Desktop runtime."
Start-Process -FilePath "$env:TEMP\windowsdesktop-runtime.exe" -ArgumentList "/install /quiet /norestart" -Wait
}

View File

@ -32,6 +32,9 @@ export function Connect() {
}
;
function applyMessageHandlers(hubConnection) {
hubConnection.on("Chat", (deviceName, message) => {
AddConsoleHTML(`<strong class="text-info">Chat from ${deviceName}</strong>: ${message}`);
});
hubConnection.on("UserOptions", (options) => {
Main.UserSettings.CommandModeShortcuts.Web = options.CommandModeShortcutWeb;
Main.UserSettings.CommandModeShortcuts.PSCore = options.CommandModeShortcutPSCore;

File diff suppressed because one or more lines are too long

View File

@ -44,6 +44,9 @@ export function Connect() {
};
function applyMessageHandlers(hubConnection) {
hubConnection.on("Chat", (deviceName: string, message: string) => {
AddConsoleHTML(`<strong class="text-info">Chat from ${deviceName}</strong>: ${message}`);
});
hubConnection.on("UserOptions", (options: UserOptions) => {
Main.UserSettings.CommandModeShortcuts.Web = options.CommandModeShortcutWeb;
Main.UserSettings.CommandModeShortcuts.PSCore = options.CommandModeShortcutPSCore;

View File

@ -7,13 +7,14 @@ import * as DataGrid from "../DataGrid.js";
import { AddConsoleHTML, AddConsoleOutput, AddTransferHarness } from "../Console.js";
import { GetSelectedDevices } from "../DataGrid.js";
var commands = [
new ConsoleCommand("Chat", [], "Start a chat session with the selected device.", "chat -message Hey, this is your IT guy.", "", (parameters, paramaterDict) => {
new ConsoleCommand("Chat", [
new Parameter("message", "The message to send to the remote device.", "String")
], "Start a chat session with the selected device.", "chat -message Hey, this is your IT guy.", "", (parameters, paramaterDict) => {
var selectedDevices = Main.DataGrid.GetSelectedDevices();
if (selectedDevices.length == 0) {
AddConsoleOutput("You must select a device first.");
return;
}
AddConsoleOutput("Sending chat message...");
BrowserSockets.Connection.invoke("Chat", paramaterDict["message"], selectedDevices.map(x => x.ID));
}),
new ConsoleCommand("DeployScript", [

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,9 @@ import { GetSelectedDevices } from "../DataGrid.js";
var commands: Array<ConsoleCommand> = [
new ConsoleCommand(
"Chat",
[],
[
new Parameter("message", "The message to send to the remote device.", "String")
],
"Start a chat session with the selected device.",
"chat -message Hey, this is your IT guy.",
"",
@ -23,7 +25,6 @@ var commands: Array<ConsoleCommand> = [
return;
}
AddConsoleOutput("Sending chat message...");
BrowserSockets.Connection.invoke("Chat", paramaterDict["message"], selectedDevices.map(x => x.ID));
}
),