Compare commits
10 commits
811fd766a9
...
6f3b692c34
Author | SHA1 | Date | |
---|---|---|---|
6f3b692c34 | |||
3ef2980212 | |||
elijahr2411 | a8254a23b5 | ||
d72094a68b | |||
elijahr2411 | c793b9f1d8 | ||
elijahr2411 | 6d4c1dcca2 | ||
542fac1ac0 | |||
af6e22f803 | |||
3e9c25b72c | |||
65c8d69434 |
36
.github/workflows/dotnet.yml
vendored
Normal file
36
.github/workflows/dotnet.yml
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# This workflow will build a .NET project
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
|
||||||
|
|
||||||
|
name: .NET
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: 6.0.x
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
- name: Build
|
||||||
|
run: dotnet build --no-restore
|
||||||
|
- name: Test
|
||||||
|
run: dotnet test --no-build --verbosity normal
|
||||||
|
- name: publish to NuGet
|
||||||
|
id: publish_nuget
|
||||||
|
uses: tedd/publish-nuget-neo@v1
|
||||||
|
with:
|
||||||
|
NUGET_KEY: ${{SECRETS.NUGET_KEY}}
|
||||||
|
PROJECT_FILE_PATH: CollabVMSharp/CollabVMSharp.csproj
|
||||||
|
PACKAGE_NAME: CollabVMSharp
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Security.Authentication;
|
using System.Security.Authentication;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
|
@ -38,6 +39,7 @@ public class CollabVMClient {
|
||||||
private TurnUpdateEventArgs _currentturn;
|
private TurnUpdateEventArgs _currentturn;
|
||||||
private VoteUpdateEventArgs _currentvote;
|
private VoteUpdateEventArgs _currentvote;
|
||||||
private WebProxy? _proxy;
|
private WebProxy? _proxy;
|
||||||
|
private Dictionary<string, Action<string, string[]>> commands;
|
||||||
// Tasks and related
|
// Tasks and related
|
||||||
private TaskCompletionSource<Node[]> GotNodeList;
|
private TaskCompletionSource<Node[]> GotNodeList;
|
||||||
private TaskCompletionSource<bool> GotConnectionToNode;
|
private TaskCompletionSource<bool> GotConnectionToNode;
|
||||||
|
@ -61,6 +63,7 @@ public class CollabVMClient {
|
||||||
public event EventHandler<ChatMessage[]> ChatHistory;
|
public event EventHandler<ChatMessage[]> ChatHistory;
|
||||||
public event EventHandler ConnectedToNode;
|
public event EventHandler ConnectedToNode;
|
||||||
public event EventHandler NodeConnectFailed;
|
public event EventHandler NodeConnectFailed;
|
||||||
|
public event EventHandler<string> ConnectionFailed;
|
||||||
public event EventHandler<RectEventArgs> Rect;
|
public event EventHandler<RectEventArgs> Rect;
|
||||||
public event EventHandler<string> Renamed;
|
public event EventHandler<string> Renamed;
|
||||||
public event EventHandler<UserRenamedEventArgs> UserRenamed;
|
public event EventHandler<UserRenamedEventArgs> UserRenamed;
|
||||||
|
@ -87,6 +90,7 @@ public class CollabVMClient {
|
||||||
}
|
}
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.node = node;
|
this.node = node;
|
||||||
|
this.commands = new();
|
||||||
this._rank = Rank.Unregistered;
|
this._rank = Rank.Unregistered;
|
||||||
this._perms = Permissions.None;
|
this._perms = Permissions.None;
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
|
@ -126,6 +130,7 @@ public class CollabVMClient {
|
||||||
ChatHistory += delegate { };
|
ChatHistory += delegate { };
|
||||||
ConnectedToNode += delegate { };
|
ConnectedToNode += delegate { };
|
||||||
NodeConnectFailed += delegate { };
|
NodeConnectFailed += delegate { };
|
||||||
|
ConnectionFailed += delegate { };
|
||||||
Rect += delegate { };
|
Rect += delegate { };
|
||||||
Renamed += delegate { };
|
Renamed += delegate { };
|
||||||
UserRenamed += delegate { };
|
UserRenamed += delegate { };
|
||||||
|
@ -140,8 +145,15 @@ public class CollabVMClient {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Connect to the CollabVM Server
|
/// Connect to the CollabVM Server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task Connect() {
|
public async Task<bool> Connect() {
|
||||||
|
try {
|
||||||
await this.socket.ConnectAsync(this.url, CancellationToken.None);
|
await this.socket.ConnectAsync(this.url, CancellationToken.None);
|
||||||
|
}
|
||||||
|
catch (WebSocketException e) {
|
||||||
|
this.ConnectionFailed.Invoke(this, e.Message);
|
||||||
|
this.Cleanup(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
this._connected = true;
|
this._connected = true;
|
||||||
if (this.username != null)
|
if (this.username != null)
|
||||||
this.SendMsg(Guacutils.Encode("rename", this.username));
|
this.SendMsg(Guacutils.Encode("rename", this.username));
|
||||||
|
@ -151,6 +163,7 @@ public class CollabVMClient {
|
||||||
this.SendMsg(Guacutils.Encode("connect", this.node));
|
this.SendMsg(Guacutils.Encode("connect", this.node));
|
||||||
this.NOPRecieve.Start();
|
this.NOPRecieve.Start();
|
||||||
this.WebSocketLoop();
|
this.WebSocketLoop();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void WebSocketLoop() {
|
private async void WebSocketLoop() {
|
||||||
|
@ -224,7 +237,11 @@ public class CollabVMClient {
|
||||||
}
|
}
|
||||||
ChatHistory.Invoke(this, msgs.ToArray());
|
ChatHistory.Invoke(this, msgs.ToArray());
|
||||||
// I should probably add a config option for whether or not the message should be HTML encoded
|
// I should probably add a config option for whether or not the message should be HTML encoded
|
||||||
} else Chat.Invoke(this, new ChatMessage {Username = msgArr[1], Message = WebUtility.HtmlDecode(msgArr[2])});
|
}
|
||||||
|
else {
|
||||||
|
Chat.Invoke(this, new ChatMessage { Username = msgArr[1], Message = WebUtility.HtmlDecode(msgArr[2]) });
|
||||||
|
this.ProcessCommand(msgArr[1], WebUtility.HtmlDecode(msgArr[2]));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "captcha": {
|
case "captcha": {
|
||||||
|
@ -791,6 +808,29 @@ public class CollabVMClient {
|
||||||
await this.SendMsg(Guacutils.Encode("admin", "23"));
|
await this.SendMsg(Guacutils.Encode("admin", "23"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a command for users on the VM to run
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cmd">The command which triggers the callback. For example, "!ban" would match "!ban guest12345"</param>
|
||||||
|
/// <param name="callback">Function to be called when a user executes the command. The first parameter is a username and the last is an array of arguments</param>
|
||||||
|
public void RegisterCommand(string cmd, Action<string, string[]> callback) {
|
||||||
|
this.commands.Add(cmd, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCommand(string username, string cmd) {
|
||||||
|
// I stole this from stackoverflow
|
||||||
|
var re = new Regex("(?<=\")[^\"]*(?=\")|[^\" ]+");
|
||||||
|
string[] args;
|
||||||
|
try {
|
||||||
|
args = re.Matches(cmd).Cast<Match>().Select(m => m.Value).ToArray();
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (commands.ContainsKey(args[0]))
|
||||||
|
commands[args[0]](username, args.Skip(1).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
public Image GetFramebuffer() => framebuffer.CloneAs<Rgba32>();
|
public Image GetFramebuffer() => framebuffer.CloneAs<Rgba32>();
|
||||||
|
|
||||||
private Task sendMouse() => this.SendMsg(Guacutils.Encode("mouse", mouse.X.ToString(), mouse.Y.ToString(), mouse.MakeMask().ToString()));
|
private Task sendMouse() => this.SendMsg(Guacutils.Encode("mouse", mouse.X.ToString(), mouse.Y.ToString(), mouse.MakeMask().ToString()));
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<LangVersion>10</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
|
<Version>2.0.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
35
README.md
Normal file
35
README.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# CollabVMSharp
|
||||||
|
|
||||||
|
CollabVM client library in C#.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
The API is well documented with XML documentation, meaning hovering over a method or property in Visual Studio or another IDE should give a pretty good idea of how to do things. For a basic usage example, see below
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using System;
|
||||||
|
using CollabVMSharp;
|
||||||
|
// Instantiate the client
|
||||||
|
var cvm = new CollabVMClient("wss://computernewb.com/collab-vm/vm0", "cvmsharptest", "vm0b0t");
|
||||||
|
// Connect to the VM
|
||||||
|
await cvm.Connect();
|
||||||
|
// Send a chat
|
||||||
|
await cvm.SendChat("What hath god wrought?");
|
||||||
|
// Add a command
|
||||||
|
cvm.RegisterCommand("!test", (username, args) => {
|
||||||
|
Console.WriteLine($"You said {String.Join(", ", args)}, {username}!");
|
||||||
|
});
|
||||||
|
// Queue a turn, wait until we get the turn
|
||||||
|
await cvm.GetTurn();
|
||||||
|
// Type a string into the VM
|
||||||
|
await cvm.TypeString("hey sexies");
|
||||||
|
// Login as an admin or mod
|
||||||
|
await cvm.Login("hunter2");
|
||||||
|
// Run a command in the QEMU monitor and get a response
|
||||||
|
Console.WriteLine(await cvm.QEMUMonitor("info block"));
|
||||||
|
// Send a message when someone takes a turn
|
||||||
|
cvm.TurnUpdate += async (_, e) => {
|
||||||
|
await cvm.SendChat($"You have the turn, {e.Queue[0].Username}!");
|
||||||
|
};
|
||||||
|
```
|
Loading…
Reference in a new issue