Compare commits

...

5 Commits

Author SHA1 Message Date
Ben Harris 3f7e13763d start adding FromHostString
continuous-integration/drone/push Build is passing Details
2024-04-23 20:22:12 -04:00
Ben Harris 44cf32618f port glob test
continuous-integration/drone/push Build is failing Details
2024-04-23 18:18:11 -04:00
Ben Harris d3c19a0987 rename test folders 2024-04-23 18:17:27 -04:00
Ben Harris 632eadf483 add .net 6 support
continuous-integration/drone/push Build is passing Details
2024-04-23 17:43:05 -04:00
Ben Harris 9fbae50b5d begin work on ircrobots 2024-04-23 17:43:03 -04:00
42 changed files with 525 additions and 24 deletions

View File

@ -0,0 +1,6 @@
using IRCRobots;
var client = new Server();
var b = new Bot();
b.AddServer("libera", new ConnectionParams() {Host = })
await b.Run();

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\IRCRobots\IRCRobots.csproj" />
</ItemGroup>
</Project>

32
IRCRobots/Bot.cs Normal file
View File

@ -0,0 +1,32 @@
using System.Threading.Tasks;
namespace IRCRobots
{
public class Bot : IBot
{
public IServer CreateServer(string name)
{
throw new System.NotImplementedException();
}
public async Task<bool> Disconnected(IServer server)
{
throw new System.NotImplementedException();
}
public async Task Disconnect(IServer server)
{
throw new System.NotImplementedException();
}
public async Task<IServer> AddServer(string name, ConnectionParams connectionParams)
{
throw new System.NotImplementedException();
}
public async Task Run()
{
throw new System.NotImplementedException();
}
}
}

30
IRCRobots/Capability.cs Normal file
View File

@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
namespace IRCRobots
{
public class Capability : ICapability
{
public string Name { get; set; }
public string DraftName { get; set; }
public string Alias { get; }
public IEnumerable<string> DependsOn { get; set; }
private IEnumerable<string> Caps { get; }
public Capability()
{
Alias ??= Name;
Caps = new List<string> {Name, DraftName};
}
public string? Available(IEnumerable<string> capabilities)
{
return Caps.FirstOrDefault(cap => cap != null && capabilities.Contains(cap));
}
public bool Match(string capability)
{
return Caps.Contains(capability);
}
}
}

View File

@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace IRCRobots
{
public class ConnectionParams
{
public string Nickname { get; set; }
public string Host { get; set; }
public int Port { get; set; }
public bool UseTLS { get; set; }
public string? Username { get; set; } = null;
public string? Realname { get; set; } = null;
public string? Bindhost { get; set; } = null;
public string? Password { get; set; } = null;
public bool VerifyTLS { get; set; } = true;
public SASLParams? SASL { get; set; } = null;
public STSPolicy? STS { get; set; } = null;
public ResumePolicy? Resume { get; set; } = null;
public int Reconnect { get; set; } = 10; // seconds
public IEnumerable<string> AltNicknames { get; set; } = new List<string>();
public IEnumerable<string> Autojoin { get; set; } = new List<string>();
public static ConnectionParams FromHostString(string nickName, string hostString)
{
var result = new ConnectionParams {Nickname = nickName};
var ipv6host = Regex.Matches(hostString, @"\[([a-fA-F0-9:]+)\]").SingleOrDefault();
var portString = "";
if (ipv6host is { Index: 0 })
{
result.Host = ipv6host.Groups[0].Value;
portString = hostString[(ipv6host.Index + ipv6host.Length)..];
}
else
{
var s = hostString.Split(':', 2);
result.Host = s[0];
portString = s[1];
}
if (string.IsNullOrWhiteSpace(portString))
{
portString = "6667";
result.UseTLS = false;
}
else
{
result.UseTLS = portString.StartsWith('+') || portString.StartsWith('~');
result.VerifyTLS = portString.StartsWith('~');
portString = portString[1..];
}
result.Port = int.Parse(portString);
return result;
}
}
}

67
IRCRobots/Glob.cs Normal file
View File

@ -0,0 +1,67 @@
using System.Text;
namespace IRCRobots
{
public class Glob
{
public string Pattern { get; set; }
public static string Collapse(string pattern)
{
var result = new StringBuilder();
var i = 0;
while (i < pattern.Length)
{
var seenAst = false;
while (i < pattern.Length && (pattern[i] == '*' || pattern[i] == '?'))
{
if (pattern[i] == '?') result.Append('?');
else if (pattern[i] == '*') seenAst = true;
i++;
}
if (seenAst) result.Append('*');
if (i < pattern.Length) result.Append(pattern[i++]);
}
return result.ToString();
}
public bool Match(string s)
{
int i = 0, j = 0;
int iBackup = -1, jBackup = -1;
while (j < s.Length)
{
// p = (pattern[i:] or [None])[0]
var p = Pattern[i];
if (p == '*')
{
i++;
iBackup = i;
jBackup = j;
}
else if (p == '?' || p == s[j])
{
i++;
j++;
}
else
{
if (iBackup == -1)
return false;
jBackup++;
j = jBackup;
i = iBackup;
}
}
return i == Pattern.Length;
}
}
}

13
IRCRobots/IBot.cs Normal file
View File

@ -0,0 +1,13 @@
using System.Threading.Tasks;
namespace IRCRobots
{
public interface IBot
{
public IServer CreateServer(string name);
public Task<bool> Disconnected(IServer server);
public Task Disconnect(IServer server);
public Task<IServer> AddServer(string name, ConnectionParams connectionParams);
public Task Run();
}
}

11
IRCRobots/ICapability.cs Normal file
View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace IRCRobots
{
public interface ICapability
{
public string? Available(IEnumerable<string> capabilities);
public bool Match(string capability);
public string Copy() => "ICapability";
}
}

View File

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<PackageId>IRCRobots</PackageId>
<Version>1.4.0</Version>
<Authors>Ben Harris</Authors>
<Company>tildeverse.org</Company>
<IsPackable>true</IsPackable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageProjectUrl>https://tildegit.org/irctokens/ircsharp</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://tildegit.org/irctokens/ircsharp/src/branch/main/IRCRobots</RepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<RepositoryType>git</RepositoryType>
<PackageTags>irc</PackageTags>
<PackageVersion>1.4.0</PackageVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
<DebugType>embedded</DebugType>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\IRCStates\IRCStates.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.Gitea" Version="8.0.0" PrivateAssets="All"/>
</ItemGroup>
</Project>

33
IRCRobots/IServer.cs Normal file
View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using IRCTokens;
namespace IRCRobots
{
public interface IServer
{
public IEnumerable<SentLine> SendRaw(string line, SendPriority sendPriority = SendPriority.Default);
public IEnumerable<SentLine> Send(Line line, SendPriority sendPriority = SendPriority.Default);
public void SetThrottle(int rate, float time);
public (string address, int port) ServerAddress();
public Task Connect(ITCPTransport transport, ConnectionParams connectionParams);
public Task Disconnect();
public void LinePreread(Line line);
public void LinePresend(Line line);
public Task LineRead(Line line);
public Task LineSend(Line line);
public Task STSPolicy(STSPolicy stsPolicy);
public Task ResumePolicy(ResumePolicy resumePolicy);
public bool CapAgreed(ICapability capability);
public string? CapAvailable(ICapability capability);
public Task<bool> SASLAuth(SASLParams saslParams);
}
public enum SendPriority
{
High = 0,
Medium = 10,
Low = 20,
Default = Medium
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace IRCRobots
{
public interface ITCPTransport
{
public Task Connect(string hostname, int port, bool useTLS, bool verifyTLS = true, string? bindhost = null);
}
}

27
IRCRobots/MessageTag.cs Normal file
View File

@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
namespace IRCRobots
{
public class MessageTag
{
public MessageTag()
{
Tags = new[] {Name, DraftName};
}
public string Name { get; set; }
public string DraftName { get; set; }
public IEnumerable<string> Tags { get; }
public string? Available(IEnumerable<string> tags)
{
return Tags.FirstOrDefault(tag => tag != null && tags.Contains(tag));
}
public string? Get(Dictionary<string, string> tags)
{
return "";
}
}
}

3
IRCRobots/README.md Normal file
View File

@ -0,0 +1,3 @@
# IRCRobots
WIP port of [jesopo/ircrobots](https://github.com/jesopo/ircrobots)

View File

@ -0,0 +1,8 @@
namespace IRCRobots
{
public class ResumePolicy
{
public string Address { get; set; }
public string Token { get; set; }
}
}

7
IRCRobots/SASLParams.cs Normal file
View File

@ -0,0 +1,7 @@
namespace IRCRobots
{
public class SASLParams
{
public string Mechanism { get; set; }
}
}

10
IRCRobots/STSPolicy.cs Normal file
View File

@ -0,0 +1,10 @@
namespace IRCRobots
{
public class STSPolicy
{
public int Created { get; set; }
public int Port { get; set; }
public int Duration { get; set; }
public bool Preload { get; set; }
}
}

11
IRCRobots/SentLine.cs Normal file
View File

@ -0,0 +1,11 @@
using IRCTokens;
namespace IRCRobots
{
public class SentLine
{
public int Id { get; set; }
public SendPriority Priority { get; set; }
public Line Line { get; set; }
}
}

90
IRCRobots/Server.cs Normal file
View File

@ -0,0 +1,90 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using IRCStates;
using IRCTokens;
namespace IRCRobots
{
public class Server : IServer
{
private int SentCount { get; set; }
public IEnumerable<SentLine> SendRaw(string line, SendPriority sendPriority = SendPriority.Default)
{
throw new System.NotImplementedException();
}
public IEnumerable<SentLine> Send(Line line, SendPriority sendPriority = SendPriority.Default)
{
LinePresend(line);
var sentLine = new SentLine {Id = SentCount, Priority = sendPriority, Line = line};
SentCount++;
return new[] {sentLine};
}
public void SetThrottle(int rate, float time)
{
throw new System.NotImplementedException();
}
public (string address, int port) ServerAddress()
{
throw new System.NotImplementedException();
}
public async Task Connect(ITCPTransport transport, ConnectionParams connectionParams)
{
throw new System.NotImplementedException();
}
public async Task Disconnect()
{
throw new System.NotImplementedException();
}
public void LinePreread(Line line)
{
throw new System.NotImplementedException();
}
public void LinePresend(Line line)
{
throw new System.NotImplementedException();
}
public async Task LineRead(Line line)
{
throw new System.NotImplementedException();
}
public async Task LineSend(Line line)
{
throw new System.NotImplementedException();
}
public async Task STSPolicy(STSPolicy stsPolicy)
{
throw new System.NotImplementedException();
}
public async Task ResumePolicy(ResumePolicy resumePolicy)
{
throw new System.NotImplementedException();
}
public bool CapAgreed(ICapability capability)
{
throw new System.NotImplementedException();
}
public string? CapAvailable(ICapability capability)
{
throw new System.NotImplementedException();
}
public async Task<bool> SASLAuth(SASLParams saslParams)
{
throw new System.NotImplementedException();
}
}
}

View File

@ -11,6 +11,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IRCRobots\IRCRobots.csproj" />
<ProjectReference Include="..\IRCStates\IRCStates.csproj" />
<ProjectReference Include="..\IRCTokens\IRCTokens.csproj" />
</ItemGroup>
@ -23,7 +24,7 @@
</ItemGroup>
<ItemGroup>
<None Update="Tokenization\Data\msg-*.yaml">
<None Update="Tokens\Data\msg-*.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

View File

@ -0,0 +1,16 @@
using IRCRobots;
namespace IRCSharp.Tests.Robots;
[TestClass]
public class GlobTests
{
[TestMethod]
public void GlobCollapse()
{
Assert.AreEqual("?*", Glob.Collapse("**?*"));
Assert.AreEqual("a?*a*", Glob.Collapse("a**?a*"));
Assert.AreEqual("????*a", Glob.Collapse("?*?*?*?*a"));
Assert.AreEqual("a?*a?*", Glob.Collapse("a*?*a?**"));
}
}

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Cap

View File

@ -1,6 +1,6 @@
// ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Casemap

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Channel

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Emit

View File

@ -1,6 +1,6 @@
// ReSharper disable InconsistentNaming
// ReSharper disable StringLiteralTypo
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class ISupport

View File

@ -1,5 +1,5 @@
// ReSharper disable StringLiteralTypo
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Mode

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Motd

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Sasl

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class User

View File

@ -1,5 +1,5 @@
// ReSharper disable StringLiteralTypo
namespace IRCSharp.Tests.State;
namespace IRCSharp.Tests.States;
[TestClass]
public class Who

View File

@ -1,6 +1,6 @@
using YamlDotNet.Serialization;
namespace IRCSharp.Tests.Tokenization.Data;
namespace IRCSharp.Tests.Tokens.Data;
public class JoinModel
{

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.Tokenization.Data;
namespace IRCSharp.Tests.Tokens.Data;
public class SplitModel
{

View File

@ -1,5 +1,5 @@
// ReSharper disable StringLiteralTypo
namespace IRCSharp.Tests.Tokenization;
namespace IRCSharp.Tests.Tokens;
[TestClass]
public class Format

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.Tokenization;
namespace IRCSharp.Tests.Tokens;
[TestClass]
public class Hostmask

View File

@ -1,9 +1,9 @@
using System.Globalization;
using IRCSharp.Tests.Tokenization.Data;
using IRCSharp.Tests.Tokens.Data;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace IRCSharp.Tests.Tokenization;
namespace IRCSharp.Tests.Tokens;
[TestClass]
public class Parser
@ -20,7 +20,7 @@ public class Parser
[TestMethod]
public void Split()
{
foreach (var test in LoadYaml<SplitModel>("Tokenization/Data/msg-split.yaml").Tests)
foreach (var test in LoadYaml<SplitModel>("Tokens/Data/msg-split.yaml").Tests)
{
var tokens = new Line(test.Input);
var atoms = test.Atoms;
@ -37,7 +37,7 @@ public class Parser
[TestMethod]
public void Join()
{
foreach (var test in LoadYaml<JoinModel>("Tokenization/Data/msg-join.yaml").Tests)
foreach (var test in LoadYaml<JoinModel>("Tokens/Data/msg-join.yaml").Tests)
{
var atoms = test.Atoms;
var line = new Line

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.Tokenization;
namespace IRCSharp.Tests.Tokens;
[TestClass]
public class StatefulDecoder

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.Tokenization;
namespace IRCSharp.Tests.Tokens;
[TestClass]
public class StatefulEncoder

View File

@ -1,4 +1,4 @@
namespace IRCSharp.Tests.Tokenization;
namespace IRCSharp.Tests.Tokens;
[TestClass]
public class Tokenization

View File

@ -20,6 +20,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRCSharp.Tests", "IRCSharp.
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{4260E03C-6E28-4519-8943-5B477841A75A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRCRobots", "IRCRobots\IRCRobots.csproj", "{BACE834E-B190-46F1-8687-E994316FF768}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robots", "Examples\Robots\Robots.csproj", "{932066DD-A44A-47EA-94F5-D7847CAA8EC7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -46,6 +50,14 @@ Global
{B420F0F3-1ED0-4FD3-9E91-2E7F96F9FF7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B420F0F3-1ED0-4FD3-9E91-2E7F96F9FF7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B420F0F3-1ED0-4FD3-9E91-2E7F96F9FF7F}.Release|Any CPU.Build.0 = Release|Any CPU
{BACE834E-B190-46F1-8687-E994316FF768}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BACE834E-B190-46F1-8687-E994316FF768}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BACE834E-B190-46F1-8687-E994316FF768}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BACE834E-B190-46F1-8687-E994316FF768}.Release|Any CPU.Build.0 = Release|Any CPU
{932066DD-A44A-47EA-94F5-D7847CAA8EC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{932066DD-A44A-47EA-94F5-D7847CAA8EC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{932066DD-A44A-47EA-94F5-D7847CAA8EC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{932066DD-A44A-47EA-94F5-D7847CAA8EC7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -53,6 +65,7 @@ Global
GlobalSection(NestedProjects) = preSolution
{A45DA39B-6B47-4713-8049-3B36E0235B67} = {4260E03C-6E28-4519-8943-5B477841A75A}
{BC9F6696-9D83-4F7A-9E15-CE4D3626C1AF} = {4260E03C-6E28-4519-8943-5B477841A75A}
{932066DD-A44A-47EA-94F5-D7847CAA8EC7} = {4260E03C-6E28-4519-8943-5B477841A75A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0B91F0EA-8564-4318-8EEC-ED0640475141}

View File

@ -1,6 +1,12 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:schemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:schemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ACK/@EntryIndexedValue">ACK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SASL/@EntryIndexedValue">SASL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=STS/@EntryIndexedValue">STS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TLS/@EntryIndexedValue">TLS</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=CHGHOST/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Invex/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=realname/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SETNAME/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=SETNAME/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Autojoin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Bindhost/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Realname/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>