Compare commits

..

1 Commits

Author SHA1 Message Date
Anna 29bdb77041 classification 2022-08-29 21:33:36 -04:00
16 changed files with 1133 additions and 1747 deletions

View File

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7</TargetFramework>
<TargetFramework>net6</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ML" Version="2.0.1" />
<PackageReference Include="Microsoft.ML" Version="1.7.1"/>
</ItemGroup>
</Project>

View File

@ -2,15 +2,19 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7</TargetFramework>
<TargetFramework>net6</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ConsoleTables" Version="2.5.0"/>
<PackageReference Include="CsvHelper" Version="30.0.1"/>
<PackageReference Include="Microsoft.ML" Version="2.0.1"/>
<PackageReference Include="ConsoleTables" Version="2.4.2"/>
<PackageReference Include="CsvHelper" Version="28.0.1"/>
<PackageReference Include="Microsoft.ML" Version="2.0.0-preview.22424.1"/>
<PackageReference Include="Microsoft.ML.TorchSharp" Version="0.20.0-preview.22424.1"/>
<PackageReference Include="MimeKitLite" Version="3.4.0"/>
<PackageReference Include="TorchSharp-cpu" Version="0.96.3"/>
<!-- <PackageReference Include="TorchSharp-cuda-linux" Version="0.96.3" />-->
</ItemGroup>
<ItemGroup>

View File

@ -10,7 +10,9 @@ using CsvHelper;
using CsvHelper.Configuration;
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.TorchSharp;
using Microsoft.ML.Transforms.Text;
using MimeKit;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using NoSoliciting.Interface;
@ -20,8 +22,6 @@ namespace NoSoliciting.Trainer {
private static readonly string[] StopWords = {
"discord",
"gg",
"twitch",
"tv",
"lgbt",
"lgbtq",
"lgbtqia",
@ -41,6 +41,7 @@ namespace NoSoliciting.Trainer {
Interactive,
InteractiveFull,
Normalise,
Import,
}
[Serializable]
@ -56,6 +57,51 @@ namespace NoSoliciting.Trainer {
public string? SuggestedClassification { get; set; }
}
private static void Import(string path) {
var allData = new List<Data>();
var opts = new ParserOptions {
CharsetEncoding = Encoding.UTF8,
};
foreach (var emlPath in Directory.GetFiles(path, "*.eml")) {
var message = MimeMessage.Load(opts, new FileStream(emlPath, FileMode.Open));
var lines = message.TextBody
.Split('\r', '\n')
.SkipWhile(line => !line.StartsWith("JSON: "))
.Select(line => line.Replace("JSON: ", "").Replace(" ", "").Trim())
.ToArray();
if (lines.Length == 0) {
continue;
}
var json = string.Join("", lines);
var jsonText = Encoding.UTF8.GetString(Convert.FromBase64String(json));
var report = JsonConvert.DeserializeObject<ReportInput>(jsonText);
var content = XivString.GetText(report.Content);
var data = new Data(report.Type, content) {
Category = report.SuggestedClassification,
};
data.Message = data.Message
.Replace("\r\n", " ")
.Replace('\r', ' ')
.Replace('\n', ' ');
allData.Add(data);
}
var writer = new StringWriter();
using var csv = new CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture) {
HeaderValidated = null,
Encoding = Encoding.UTF8,
});
csv.WriteRecords(allData
.OrderBy(data => data.Category)
.ThenBy(data => data.Channel)
.ThenBy(data => data.Message));
Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine(writer.ToString());
}
private static void Main(string[] args) {
var mode = args[0] switch {
"test" => Mode.Test,
@ -63,9 +109,15 @@ namespace NoSoliciting.Trainer {
"interactive" => Mode.Interactive,
"interactive-full" => Mode.InteractiveFull,
"normalise" => Mode.Normalise,
"import" => Mode.Import,
_ => throw new ArgumentException("invalid argument"),
};
if (mode == Mode.Import) {
Import(args[1]);
return;
}
if (mode == Mode.Normalise) {
Console.WriteLine("Ready");
@ -160,12 +212,12 @@ namespace NoSoliciting.Trainer {
.Append(ctx.Transforms.CustomMapping(compute.GetMapping(), "Compute"))
.Append(ctx.Transforms.CustomMapping(normalise.GetMapping(), "Normalise"))
.Append(ctx.Transforms.Text.NormalizeText("MsgNormal", nameof(Data.Normalise.Normalised.NormalisedMessage), keepPunctuations: false, keepNumbers: false))
.Append(ctx.Transforms.Text.TokenizeIntoWords("MsgTokens", "MsgNormal"))
.Append(ctx.Transforms.Text.RemoveDefaultStopWords("MsgNoDefStop", "MsgTokens"))
.Append(ctx.Transforms.Text.RemoveStopWords("MsgNoStop", "MsgNoDefStop", StopWords))
.Append(ctx.Transforms.Conversion.MapValueToKey("MsgKey", "MsgNoStop"))
.Append(ctx.Transforms.Text.ProduceNgrams("MsgNgrams", "MsgKey", weighting: NgramExtractingEstimator.WeightingCriteria.Tf))
.Append(ctx.Transforms.NormalizeLpNorm("FeaturisedMessage", "MsgNgrams"))
// .Append(ctx.Transforms.Text.TokenizeIntoWords("MsgTokens", "MsgNormal"))
// .Append(ctx.Transforms.Text.RemoveDefaultStopWords("MsgNoDefStop", "MsgTokens"))
// .Append(ctx.Transforms.Text.RemoveStopWords("MsgNoStop", "MsgNoDefStop", StopWords))
// .Append(ctx.Transforms.Conversion.MapValueToKey("MsgKey", "MsgNoStop"))
// .Append(ctx.Transforms.Text.ProduceNgrams("MsgNgrams", "MsgKey", weighting: NgramExtractingEstimator.WeightingCriteria.Tf))
// .Append(ctx.Transforms.NormalizeLpNorm("FeaturisedMessage", "MsgNgrams"))
.Append(ctx.Transforms.Conversion.ConvertType("CPartyFinder", nameof(Data.Computed.PartyFinder)))
.Append(ctx.Transforms.Conversion.ConvertType("CShout", nameof(Data.Computed.Shout)))
.Append(ctx.Transforms.Conversion.ConvertType("CTrade", nameof(Data.Computed.ContainsTradeWords)))
@ -173,10 +225,9 @@ namespace NoSoliciting.Trainer {
.Append(ctx.Transforms.Conversion.ConvertType("HasWard", nameof(Data.Computed.ContainsWard)))
.Append(ctx.Transforms.Conversion.ConvertType("HasPlot", nameof(Data.Computed.ContainsPlot)))
.Append(ctx.Transforms.Conversion.ConvertType("HasNumbers", nameof(Data.Computed.ContainsHousingNumbers)))
.Append(ctx.Transforms.Concatenate("Features", "FeaturisedMessage", "CPartyFinder", "CShout", "CTrade", "HasWard", "HasPlot", "HasNumbers", "CSketch"))
// macro 81.8 micro 84.6 (Tf weighting) - slow
// .Append(ctx.MulticlassClassification.Trainers.SdcaMaximumEntropy(exampleWeightColumnName: "Weight", l1Regularization: 0, l2Regularization: 0, maximumNumberOfIterations: 2_500))
.Append(ctx.MulticlassClassification.Trainers.SdcaMaximumEntropy(exampleWeightColumnName: "Weight", l1Regularization: 0, l2Regularization: 0, maximumNumberOfIterations: null))
// .Append(ctx.Transforms.Concatenate("Features", "FeaturisedMessage", "CPartyFinder", "CShout", "CTrade", "HasWard", "HasPlot", "HasNumbers", "CSketch"))
// .Append(ctx.MulticlassClassification.Trainers.SdcaMaximumEntropy(exampleWeightColumnName: "Weight"))
.Append(ctx.MulticlassClassification.Trainers.TextClassification(sentence1ColumnName: "MsgNormal"))
.Append(ctx.Transforms.Conversion.MapKeyToValue("PredictedLabel"));
var train = mode switch {

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,7 @@ namespace NoSoliciting {
private void OnCommand(string command, string args) {
if (command == "/prmt") {
this.Plugin.ChatGui.PrintError($"[{Plugin.Name}] The /prmt command is deprecated and will be removed. Please use /nosol instead.");
this.Plugin.ChatGui.PrintError($"[{this.Plugin.Name}] The /prmt command is deprecated and will be removed. Please use /nosol instead.");
}
if (args == "report") {

View File

@ -2,6 +2,7 @@
using Dalamud.Game.Gui.PartyFinder.Types;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Logging;
using NoSoliciting.Interface;
using NoSoliciting.Ml;
@ -103,10 +104,10 @@ namespace NoSoliciting {
args.Visible = false;
if (this.Plugin.Config.LogFilteredPfs) {
Plugin.Log.Info($"Filtered PF listing from {listing.Name.TextValue} ({reason}): {listing.Description.TextValue}");
PluginLog.Log($"Filtered PF listing from {listing.Name.TextValue} ({reason}): {listing.Description.TextValue}");
}
} catch (Exception ex) {
Plugin.Log.Error($"Error in PF listing event: {ex}");
PluginLog.LogError($"Error in PF listing event: {ex}");
}
}
@ -165,7 +166,7 @@ namespace NoSoliciting {
this.Plugin.AddMessageHistory(history);
if (filter && this.Plugin.Config.LogFilteredChat) {
Plugin.Log.Info($"Filtered chat message ({history.FilterReason ?? "unknown"}): {text}");
PluginLog.Log($"Filtered chat message ({history.FilterReason ?? "unknown"}): {text}");
}
return filter;
@ -201,7 +202,7 @@ namespace NoSoliciting {
var category = this.Plugin.MlFilter.ClassifyMessage((ushort) ChatType.None, desc);
if (category != MessageCategory.Normal && this.Plugin.Config.MlEnabledOn(category, ChatType.None)) {
return (category, Enum.GetName(category));
return (category, null);
}
return (null, null);

View File

@ -1,9 +1,9 @@
using Lumina.Excel.GeneratedSheets;
using Dalamud.Data;
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Dalamud.Plugin.Services;
using NoSoliciting.Ml;
namespace NoSoliciting {
@ -87,7 +87,7 @@ namespace NoSoliciting {
return null;
}
public static int MaxItemLevelAttainable(IDataManager data) {
public static int MaxItemLevelAttainable(DataManager data) {
if (MaxItemLevel > 0) {
return MaxItemLevel;
}

View File

@ -6,6 +6,7 @@ using System.Numerics;
using System.Threading.Tasks;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Logging;
using ImGuiNET;
using NoSoliciting.Ml;
using NoSoliciting.Resources;
@ -65,7 +66,7 @@ namespace NoSoliciting.Interface {
ImGui.SetNextWindowSize(new Vector2(1_000, 350), ImGuiCond.FirstUseEver);
var windowTitle = string.Format(Language.Reporting, Plugin.Name);
var windowTitle = string.Format(Language.Reporting, this.Plugin.Name);
if (!ImGui.Begin($"{windowTitle}###NoSoliciting reporting", ref this._showReporting)) {
ImGui.End();
return;
@ -223,7 +224,7 @@ namespace NoSoliciting.Interface {
ImGui.SetNextWindowSize(new Vector2(350, -1));
var modalTitle = string.Format(Language.ReportModalTitle, Plugin.Name);
var modalTitle = string.Format(Language.ReportModalTitle, this.Plugin.Name);
if (!ImGui.BeginPopupModal($"{modalTitle}###modal-message-{message.Id}")) {
return false;
}
@ -440,11 +441,11 @@ namespace NoSoliciting.Interface {
var status = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
if (status == ReportStatus.Failure) {
Plugin.Log.Warning($"Failed to report message:\n{resp}");
PluginLog.LogWarning($"Failed to report message:\n{resp}");
}
this.LastReportStatus = status;
Plugin.Log.Info(resp == null
PluginLog.Log(resp == null
? "Report not sent. ML model not set."
: $"Report sent. Response: {resp}");

View File

@ -44,7 +44,7 @@ namespace NoSoliciting.Interface {
}
public void Draw() {
var windowTitle = string.Format(Language.Settings, Plugin.Name);
var windowTitle = string.Format(Language.Settings, this.Plugin.Name);
if (!this.ShowSettings || !ImGui.Begin($"{windowTitle}###NoSoliciting settings", ref this._showSettings)) {
return;
}

View File

@ -3,9 +3,9 @@ using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Data;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
using NoSoliciting.Ml;
@ -208,7 +208,7 @@ namespace NoSoliciting {
_ => (byte) type,
};
public static string Name(this ChatType type, IDataManager data) {
public static string Name(this ChatType type, DataManager data) {
switch (type) {
case ChatType.None:
return "Party Finder";

View File

@ -5,6 +5,7 @@ using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Dalamud.Logging;
using NoSoliciting.Interface;
using NoSoliciting.Resources;
using YamlDotNet.Core;
@ -42,7 +43,7 @@ namespace NoSoliciting.Ml {
return (MessageCategory) category;
}
Plugin.Log.Warning($"Unknown message category: {prediction}");
PluginLog.LogWarning($"Unknown message category: {prediction}");
return MessageCategory.Normal;
}
@ -52,7 +53,7 @@ namespace NoSoliciting.Ml {
// download and parse the remote manifest
var manifest = await DownloadManifest();
if (manifest == null) {
Plugin.Log.Warning("Could not download manifest. Will attempt to fall back on cached version.");
PluginLog.LogWarning("Could not download manifest. Will attempt to fall back on cached version.");
}
// model zip file data
@ -95,7 +96,7 @@ namespace NoSoliciting.Ml {
var hash = sha.ComputeHash(data);
while (!hash.SequenceEqual(correctHash) && retries < maxRetries) {
Plugin.Log.Warning($"Model checksum did not match. Redownloading (attempt {retries + 1}/{maxRetries})");
PluginLog.Warning($"Model checksum did not match. Redownloading (attempt {retries + 1}/{maxRetries})");
retries += 1;
data = await DownloadModel(manifest!.Value.manifest!.ModelUrl);
@ -137,8 +138,8 @@ namespace NoSoliciting.Ml {
var data = await client.DownloadDataTaskAsync(url);
return data;
} catch (WebException e) {
Plugin.Log.Error("Could not download newest model.");
Plugin.Log.Error(e.ToString());
PluginLog.LogError("Could not download newest model.");
PluginLog.LogError(e.ToString());
LastError = e.Message;
return null;
}
@ -166,8 +167,8 @@ namespace NoSoliciting.Ml {
LastError = null;
return (LoadYaml<Manifest>(data), data);
} catch (Exception e) when (e is WebException or YamlException) {
Plugin.Log.Error("Could not download newest model manifest.");
Plugin.Log.Error(e.ToString());
PluginLog.LogError("Could not download newest model manifest.");
PluginLog.LogError(e.ToString());
LastError = e.Message;
return null;
}

View File

@ -3,8 +3,8 @@
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Version>3.0.8</Version>
<TargetFramework>net7-windows</TargetFramework>
<Version>3.0.3</Version>
<TargetFramework>net6-windows</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>x64</PlatformTarget>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@ -47,13 +47,13 @@
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dalamud.ContextMenu" Version="1.3.1"/>
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
<PackageReference Include="Fody" Version="6.8.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.ML" Version="2.0.1"/>
<PackageReference Include="Resourcer.Fody" Version="1.8.1" PrivateAssets="all"/>
<PackageReference Include="XivCommon" Version="9.0.0"/>
<PackageReference Include="YamlDotNet" Version="13.4.0"/>
<PackageReference Include="Dalamud.ContextMenu" Version="1.2.0"/>
<PackageReference Include="DalamudPackager" Version="2.1.8"/>
<PackageReference Include="Fody" Version="6.6.3" PrivateAssets="all"/>
<PackageReference Include="Microsoft.ML" Version="1.7.1"/>
<PackageReference Include="Resourcer.Fody" Version="1.8.0" PrivateAssets="all"/>
<PackageReference Include="XivCommon" Version="6.0.0"/>
<PackageReference Include="YamlDotNet" Version="12.0.0"/>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Language.resx">

View File

@ -1,4 +1,4 @@
author: Anna
author: ascclemens
name: NoSoliciting
punchline: Adblock for FFXIV.
description: |-
@ -14,6 +14,6 @@ description: |-
- Trade ads
- Community ads
- Any PF with an item level over the max
repo_url: https://git.anna.lgbt/anna/NoSoliciting
repo_url: https://git.sr.ht/~jkcclemens/NoSoliciting
# Use higher priority to filter messages before other plugins get them.
load_priority: 100

View File

@ -7,8 +7,14 @@ using System.Reflection;
using System.Threading.Tasks;
using Dalamud;
using Dalamud.ContextMenu;
using Dalamud.Data;
using Dalamud.Game.ClientState;
using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Dalamud.Game.Gui.PartyFinder;
using Dalamud.Game.Gui.Toast;
using Dalamud.IoC;
using Dalamud.Plugin.Services;
using Dalamud.Logging;
using NoSoliciting.Interface;
using NoSoliciting.Ml;
using NoSoliciting.Resources;
@ -19,33 +25,30 @@ namespace NoSoliciting {
public class Plugin : IDalamudPlugin {
private bool _disposedValue;
internal static string Name => "NoSoliciting";
public string Name => "NoSoliciting";
private Filter Filter { get; }
[PluginService]
internal static IPluginLog Log { get; private set; } = null!;
[PluginService]
internal DalamudPluginInterface Interface { get; init; } = null!;
[PluginService]
private IClientState ClientState { get; init; } = null!;
private ClientState ClientState { get; init; } = null!;
[PluginService]
internal IChatGui ChatGui { get; init; } = null!;
internal ChatGui ChatGui { get; init; } = null!;
[PluginService]
internal IPartyFinderGui PartyFinderGui { get; init; } = null!;
internal PartyFinderGui PartyFinderGui { get; init; } = null!;
[PluginService]
internal IDataManager DataManager { get; init; } = null!;
internal DataManager DataManager { get; init; } = null!;
[PluginService]
internal ICommandManager CommandManager { get; init; } = null!;
internal CommandManager CommandManager { get; init; } = null!;
[PluginService]
internal IToastGui ToastGui { get; init; } = null!;
internal ToastGui ToastGui { get; init; } = null!;
internal PluginConfiguration Config { get; }
internal XivCommonBase Common { get; }
@ -77,8 +80,8 @@ namespace NoSoliciting {
this.ConfigureLanguage();
this.Interface.LanguageChanged += this.OnLanguageUpdate;
this.Common = new XivCommonBase(this.Interface, Hooks.PartyFinderListings);
this.DalamudContextMenu = new DalamudContextMenu(this.Interface);
this.Common = new XivCommonBase(Hooks.PartyFinderListings);
this.DalamudContextMenu = new DalamudContextMenu();
this.Ui = new PluginUi(this);
this.Commands = new Commands(this);
@ -92,7 +95,7 @@ namespace NoSoliciting {
try {
FilterUtil.MaxItemLevelAttainable(this.DataManager);
} catch (Exception ex) {
Plugin.Log.Error(ex, "Exception while computing max item level");
PluginLog.LogError(ex, "Exception while computing max item level");
}
}
@ -149,7 +152,7 @@ namespace NoSoliciting {
}
this.MlStatus = MlFilterStatus.Initialised;
Log.Info("Machine learning model loaded");
PluginLog.Log("Machine learning model loaded");
});
}

View File

@ -44,17 +44,13 @@ namespace NoSoliciting {
public Dictionary<MessageCategory, HashSet<ChatType>> MlFilters { get; set; } = new() {
[MessageCategory.RmtGil] = new HashSet<ChatType> {
ChatType.None,
ChatType.Say,
ChatType.Shout,
},
[MessageCategory.RmtContent] = new HashSet<ChatType> {
ChatType.None,
ChatType.Say,
ChatType.Shout,
},
[MessageCategory.Phishing] = new HashSet<ChatType> {
ChatType.None,
ChatType.TellIncoming,
},
[MessageCategory.Roleplaying] = new HashSet<ChatType> {
@ -76,18 +72,12 @@ namespace NoSoliciting {
},
[MessageCategory.Trade] = new HashSet<ChatType> {
ChatType.None,
ChatType.Shout,
ChatType.Yell,
},
[MessageCategory.Community] = new HashSet<ChatType> {
ChatType.None,
ChatType.Shout,
ChatType.Yell,
},
[MessageCategory.Fluff] = new HashSet<ChatType> {
ChatType.None,
ChatType.Shout,
ChatType.Yell,
},
};

File diff suppressed because it is too large Load Diff