refactor: move to net5
This commit is contained in:
parent
d8e4ccd7c4
commit
3511693208
|
@ -1,2 +1,3 @@
|
|||
* text eol=lf
|
||||
*.dll binary
|
||||
*.png binary
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
using System.Text;
|
||||
using JKang.IpcServiceFramework;
|
||||
using JKang.IpcServiceFramework.Services;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public class BetterIpcSerialiser : IIpcMessageSerializer {
|
||||
private static readonly JsonSerializerSettings Settings = new() {
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
};
|
||||
|
||||
public byte[] SerializeRequest(IpcRequest request) {
|
||||
var json = JsonConvert.SerializeObject(request, Formatting.None, Settings);
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
|
||||
public IpcResponse? DeserializeResponse(byte[] binary) {
|
||||
var json = Encoding.UTF8.GetString(binary);
|
||||
return JsonConvert.DeserializeObject<IpcResponse>(json, Settings);
|
||||
}
|
||||
|
||||
public IpcRequest? DeserializeRequest(byte[] binary) {
|
||||
var json = Encoding.UTF8.GetString(binary);
|
||||
return JsonConvert.DeserializeObject<IpcRequest>(json, Settings);
|
||||
}
|
||||
|
||||
public byte[] SerializeResponse(IpcResponse response) {
|
||||
var json = JsonConvert.SerializeObject(response, Formatting.None, Settings);
|
||||
return Encoding.UTF8.GetBytes(json);
|
||||
}
|
||||
}
|
||||
}
|
7
NoSoliciting.Internal.Interface/Data.cs → NoSoliciting.Interface/Data.cs
Normal file → Executable file
7
NoSoliciting.Internal.Interface/Data.cs → NoSoliciting.Interface/Data.cs
Normal file → Executable file
|
@ -6,9 +6,8 @@ using System.Linq;
|
|||
using System.Text.RegularExpressions;
|
||||
using Microsoft.ML.Data;
|
||||
using Microsoft.ML.Transforms;
|
||||
using NoSoliciting.Interface;
|
||||
|
||||
namespace NoSoliciting.Internal.Interface {
|
||||
namespace NoSoliciting.Interface {
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")]
|
||||
|
@ -127,7 +126,7 @@ namespace NoSoliciting.Internal.Interface {
|
|||
var normalised = NoSolUtil.Normalise(this.Message);
|
||||
|
||||
output.PartyFinder = this.Channel == 0;
|
||||
output.Shout = this.Channel == 11 || this.Channel == 30;
|
||||
output.Shout = this.Channel is 11 or 30;
|
||||
output.ContainsWard = WardWords.Any(word => word.IsMatch(normalised));
|
||||
output.ContainsPlot = PlotWords.Any(word => word.IsMatch(normalised));
|
||||
output.ContainsHousingNumbers = NumbersRegex.IsMatch(normalised);
|
||||
|
@ -145,7 +144,7 @@ namespace NoSoliciting.Internal.Interface {
|
|||
public string Category { get; set; } = "UNKNOWN";
|
||||
|
||||
[ColumnName("Score")]
|
||||
public float[] Probabilities { get; set; } = new float[0];
|
||||
public float[] Probabilities { get; set; } = Array.Empty<float>();
|
||||
}
|
||||
|
||||
internal static class Ext {
|
|
@ -1,5 +1,7 @@
|
|||
namespace NoSoliciting.Interface {
|
||||
public interface IClassifier {
|
||||
using System;
|
||||
|
||||
namespace NoSoliciting.Interface {
|
||||
public interface IClassifier : IDisposable {
|
||||
void Initialise(byte[] data);
|
||||
|
||||
string Classify(ushort channel, string message);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net5</TargetFrameworks>
|
||||
<TargetFramework>net5</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JKang.IpcServiceFramework.Core" Version="3.1.0"/>
|
||||
<PackageReference Include="Microsoft.ML" Version="1.6.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net5;net48</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ML" Version="1.6.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NoSoliciting.Interface\NoSoliciting.Interface.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,24 +0,0 @@
|
|||
using System;
|
||||
using Dalamud.Game.Command;
|
||||
|
||||
namespace NoSoliciting.Lite {
|
||||
public class Commands : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
internal Commands(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.Interface.CommandManager.AddHandler("/nolite", new CommandInfo(this.OnCommand) {
|
||||
HelpMessage = "Open the NoSol Lite config",
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.CommandManager.RemoveHandler("/nolite");
|
||||
}
|
||||
|
||||
private void OnCommand(string command, string args) {
|
||||
this.Plugin.Ui.ToggleConfig();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Plugin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NoSoliciting.Lite {
|
||||
[Serializable]
|
||||
internal class Configuration : IPluginConfiguration {
|
||||
private DalamudPluginInterface Interface { get; set; } = null!;
|
||||
|
||||
public int Version { get; set; } = 1;
|
||||
|
||||
public bool CustomChatFilter { get; set; }
|
||||
public List<string> ChatSubstrings { get; } = new();
|
||||
public List<string> ChatRegexes { get; } = new();
|
||||
|
||||
[JsonIgnore]
|
||||
public List<Regex> CompiledChatRegexes { get; private set; } = new();
|
||||
|
||||
public bool CustomPFFilter { get; set; }
|
||||
public List<string> PFSubstrings { get; } = new();
|
||||
public List<string> PFRegexes { get; } = new();
|
||||
|
||||
[JsonIgnore]
|
||||
public List<Regex> CompiledPfRegexes { get; private set; } = new();
|
||||
|
||||
public bool LogFilteredPfs { get; set; } = true;
|
||||
public bool LogFilteredChat { get; set; } = true;
|
||||
|
||||
public bool ConsiderPrivatePfs { get; set; }
|
||||
|
||||
public IEnumerable<string> ValidChatSubstrings => this.ChatSubstrings.Where(needle => !string.IsNullOrWhiteSpace(needle));
|
||||
public IEnumerable<string> ValidPfSubstrings => this.PFSubstrings.Where(needle => !string.IsNullOrWhiteSpace(needle));
|
||||
|
||||
public void Initialise(DalamudPluginInterface pi) {
|
||||
this.Interface = pi;
|
||||
this.CompileRegexes();
|
||||
}
|
||||
|
||||
public void Save() {
|
||||
this.Interface.SavePluginConfig(this);
|
||||
}
|
||||
|
||||
public void CompileRegexes() {
|
||||
this.CompiledChatRegexes = this.ChatRegexes
|
||||
.Where(reg => !string.IsNullOrWhiteSpace(reg))
|
||||
.Select(reg => new Regex(reg, RegexOptions.Compiled))
|
||||
.ToList();
|
||||
this.CompiledPfRegexes = this.PFRegexes
|
||||
.Where(reg => !string.IsNullOrWhiteSpace(reg))
|
||||
.Select(reg => new Regex(reg, RegexOptions.Compiled))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
<Project>
|
||||
<Target Name="PackagePlugin" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
|
||||
<DalamudPackager ProjectDir="$(ProjectDir)"
|
||||
OutputPath="$(OutputPath)"
|
||||
AssemblyName="$(AssemblyName)"
|
||||
VersionComponents="3"
|
||||
MakeZip="true"
|
||||
Include="NoSoliciting.Lite.json;NoSoliciting.Lite.dll;NoSoliciting.Lite.pdb"/>
|
||||
</Target>
|
||||
</Project>
|
|
@ -1,9 +0,0 @@
|
|||
using System.Globalization;
|
||||
|
||||
namespace NoSoliciting.Lite {
|
||||
internal static class Extensions {
|
||||
internal static bool ContainsIgnoreCase(this string haystack, string needle) {
|
||||
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(haystack, needle, CompareOptions.IgnoreCase) >= 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.Internal.Gui;
|
||||
using Dalamud.Game.Internal.Gui.Structs;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Plugin;
|
||||
|
||||
namespace NoSoliciting.Lite {
|
||||
public class Filter : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
internal Filter(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.Interface.Framework.Gui.Chat.OnChatMessage += this.OnChat;
|
||||
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing += this.ReceiveListing;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing -= this.ReceiveListing;
|
||||
this.Plugin.Interface.Framework.Gui.Chat.OnChatMessage -= this.OnChat;
|
||||
}
|
||||
|
||||
private void OnChat(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) {
|
||||
if (isHandled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var text = message.TextValue;
|
||||
|
||||
isHandled = this.Plugin.Config.ValidChatSubstrings.Any(needle => text.ContainsIgnoreCase(needle))
|
||||
|| this.Plugin.Config.CompiledChatRegexes.Any(needle => needle.IsMatch(text));
|
||||
|
||||
if (this.Plugin.Config.LogFilteredChat && isHandled) {
|
||||
PluginLog.Log($"Filtered chat message: {text}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceiveListing(PartyFinderListing listing, PartyFinderListingEventArgs args) {
|
||||
if (!args.Visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (listing[SearchAreaFlags.Private] && !this.Plugin.Config.ConsiderPrivatePfs) {
|
||||
return;
|
||||
}
|
||||
|
||||
var text = listing.Description.TextValue;
|
||||
|
||||
args.Visible = !(this.Plugin.Config.ValidPfSubstrings.Any(needle => text.ContainsIgnoreCase(needle))
|
||||
|| this.Plugin.Config.CompiledPfRegexes.Any(needle => needle.IsMatch(text)));
|
||||
|
||||
if (this.Plugin.Config.LogFilteredPfs && !args.Visible) {
|
||||
PluginLog.Log($"Filtered PF: {text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<ResourcesMerge/>
|
||||
</Weavers>
|
|
@ -1,44 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImGui.NET">
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ImGuiScene">
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\Language.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Language.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\Language.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Language.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="1.2.1"/>
|
||||
<PackageReference Include="Fody" Version="6.5.2" PrivateAssets="all"/>
|
||||
<PackageReference Include="ResourcesMerge.Fody" Version="1.0.3" PrivateAssets="all"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,9 +0,0 @@
|
|||
author: ascclemens
|
||||
name: NoSoliciting Lite
|
||||
description: |-
|
||||
Customisable, simple chat and Party Finder filtering.
|
||||
|
||||
Only filters out text that you configure.
|
||||
repo_url: https://git.sr.ht/~jkcclemens/NoSoliciting
|
||||
# Use higher priority to filter messages before other plugins get them.
|
||||
load_priority: 100
|
|
@ -1,42 +0,0 @@
|
|||
using System.Globalization;
|
||||
using Dalamud.Plugin;
|
||||
using NoSoliciting.Lite.Resources;
|
||||
|
||||
namespace NoSoliciting.Lite {
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class Plugin : IDalamudPlugin {
|
||||
public string Name => "NoSoliciting Lite";
|
||||
|
||||
internal DalamudPluginInterface Interface { get; private set; } = null!;
|
||||
internal Configuration Config { get; private set; } = null!;
|
||||
internal PluginUi Ui { get; private set; } = null!;
|
||||
private Commands Commands { get; set; } = null!;
|
||||
private Filter Filter { get; set; } = null!;
|
||||
|
||||
public void Initialize(DalamudPluginInterface pluginInterface) {
|
||||
this.Interface = pluginInterface;
|
||||
|
||||
this.ConfigureLanguage();
|
||||
this.Interface.OnLanguageChanged += this.ConfigureLanguage;
|
||||
|
||||
this.Config = this.Interface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||
this.Config.Initialise(this.Interface);
|
||||
|
||||
this.Filter = new Filter(this);
|
||||
this.Ui = new PluginUi(this);
|
||||
this.Commands = new Commands(this);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Commands.Dispose();
|
||||
this.Ui.Dispose();
|
||||
this.Filter.Dispose();
|
||||
this.Interface.OnLanguageChanged -= this.ConfigureLanguage;
|
||||
}
|
||||
|
||||
private void ConfigureLanguage(string? langCode = null) {
|
||||
langCode ??= this.Interface.UiLanguage;
|
||||
Language.Culture = new CultureInfo(langCode ?? "en");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Text.RegularExpressions;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using NoSoliciting.Lite.Resources;
|
||||
|
||||
namespace NoSoliciting.Lite {
|
||||
public class PluginUi : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
|
||||
private bool _showWindow;
|
||||
|
||||
internal PluginUi(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.Interface.UiBuilder.OnBuildUi += this.Draw;
|
||||
this.Plugin.Interface.UiBuilder.OnOpenConfigUi += this.ToggleConfig;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.UiBuilder.OnOpenConfigUi -= this.ToggleConfig;
|
||||
this.Plugin.Interface.UiBuilder.OnBuildUi -= this.Draw;
|
||||
}
|
||||
|
||||
internal void ToggleConfig(object? sender = null, object? args = null) {
|
||||
this._showWindow = !this._showWindow;
|
||||
}
|
||||
|
||||
private void Draw() {
|
||||
if (!this._showWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.SetNextWindowSize(new Vector2(550, 350), ImGuiCond.FirstUseEver);
|
||||
|
||||
var windowTitle = string.Format(Language.Settings, this.Plugin.Name);
|
||||
if (!ImGui.Begin($"{windowTitle}###nosol-lite-settings", ref this._showWindow)) {
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
||||
var shouldSave = false;
|
||||
|
||||
if (ImGui.BeginTabBar("nosol-lite-tabs")) {
|
||||
if (ImGui.BeginTabItem("Chat")) {
|
||||
var customChat = this.Plugin.Config.CustomChatFilter;
|
||||
if (ImGui.Checkbox(Language.EnableCustomChatFilters, ref customChat)) {
|
||||
this.Plugin.Config.CustomChatFilter = customChat;
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
if (this.Plugin.Config.CustomChatFilter) {
|
||||
var substrings = this.Plugin.Config.ChatSubstrings;
|
||||
var regexes = this.Plugin.Config.ChatRegexes;
|
||||
this.DrawCustom("chat", ref shouldSave, ref substrings, ref regexes);
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Party Finder")) {
|
||||
var considerPrivate = this.Plugin.Config.ConsiderPrivatePfs;
|
||||
if (ImGui.Checkbox(Language.FilterPrivatePfs, ref considerPrivate)) {
|
||||
this.Plugin.Config.ConsiderPrivatePfs = considerPrivate;
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
var customPf = this.Plugin.Config.CustomPFFilter;
|
||||
if (ImGui.Checkbox(Language.EnableCustomPartyFinderFilters, ref customPf)) {
|
||||
this.Plugin.Config.CustomPFFilter = customPf;
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
if (this.Plugin.Config.CustomPFFilter) {
|
||||
var substrings = this.Plugin.Config.PFSubstrings;
|
||||
var regexes = this.Plugin.Config.PFRegexes;
|
||||
this.DrawCustom("pf", ref shouldSave, ref substrings, ref regexes);
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
ImGui.EndTabBar();
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
|
||||
if (!shouldSave) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Plugin.Config.Save();
|
||||
this.Plugin.Config.CompileRegexes();
|
||||
}
|
||||
|
||||
private void DrawCustom(string name, ref bool shouldSave, ref List<string> substrings, ref List<string> regexes) {
|
||||
ImGui.Columns(2);
|
||||
|
||||
ImGui.TextUnformatted(Language.SubstringsToFilter);
|
||||
if (ImGui.BeginChild($"##{name}-substrings", new Vector2(0, 175))) {
|
||||
for (var i = 0; i < substrings.Count; i++) {
|
||||
var input = substrings[i];
|
||||
if (ImGui.InputText($"##{name}-substring-{i}", ref input, 1_000)) {
|
||||
substrings[i] = input;
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconString()}##{name}-substring-{i}-remove")) {
|
||||
substrings.RemoveAt(i);
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconString()}##{name}-substring-add")) {
|
||||
substrings.Add("");
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.NextColumn();
|
||||
|
||||
ImGui.TextUnformatted(Language.RegularExpressionsToFilter);
|
||||
if (ImGui.BeginChild($"##{name}-regexes", new Vector2(0, 175))) {
|
||||
for (var i = 0; i < regexes.Count; i++) {
|
||||
var input = regexes[i];
|
||||
if (ImGui.InputText($"##{name}-regex-{i}", ref input, 1_000)) {
|
||||
try {
|
||||
_ = new Regex(input);
|
||||
// update if valid
|
||||
regexes[i] = input;
|
||||
shouldSave = true;
|
||||
} catch (ArgumentException) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconString()}##{name}-regex-{i}-remove")) {
|
||||
regexes.RemoveAt(i);
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
|
||||
ImGui.PushFont(UiBuilder.IconFont);
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconString()}##{name}-regex-add")) {
|
||||
regexes.Add("");
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
ImGui.Columns(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace NoSoliciting.Lite.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Language {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Language() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NoSoliciting.Lite.Resources.Language", typeof(Language).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enable custom chat filters.
|
||||
/// </summary>
|
||||
internal static string EnableCustomChatFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("EnableCustomChatFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enable custom Party Finder filters.
|
||||
/// </summary>
|
||||
internal static string EnableCustomPartyFinderFilters {
|
||||
get {
|
||||
return ResourceManager.GetString("EnableCustomPartyFinderFilters", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Apply filters to private Party Finder listings.
|
||||
/// </summary>
|
||||
internal static string FilterPrivatePfs {
|
||||
get {
|
||||
return ResourceManager.GetString("FilterPrivatePfs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Regular expressions to filter.
|
||||
/// </summary>
|
||||
internal static string RegularExpressionsToFilter {
|
||||
get {
|
||||
return ResourceManager.GetString("RegularExpressionsToFilter", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to {0} settings.
|
||||
/// </summary>
|
||||
internal static string Settings {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Substrings to filter.
|
||||
/// </summary>
|
||||
internal static string SubstringsToFilter {
|
||||
get {
|
||||
return ResourceManager.GetString("SubstringsToFilter", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Benutzerdefinierte Chat Filter aktivieren</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Benutzerdefinierte Gruppensuche Filter aktivieren</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Filter auf private Gruppensuchen anwenden</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>zu filternde reguläre Ausdrücke</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Einstellungen</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>zu filternde Zeichenketten</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,32 +0,0 @@
|
|||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Habilitar filtros de chat personalizados</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Habilitar filtros del Party Finder personalizados</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Aplicar filtros a listados privados del Party Finder</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Expresiones regulares a filtrar</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Ajustes de {0}</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Subcadenas a filtrar</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,32 +0,0 @@
|
|||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Activer les filtres de discussion personnalisés</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Activer les filtres de Recherche d'Équipe (PFs) personnalisés</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Appliquer les filtres aux Recherches d'Équipe (PFs) listées en privées</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Expressions régulières à filtrer</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Paramètres de {0}</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Sous-chaînes (substrings) à filtrer</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,32 +0,0 @@
|
|||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>カスタムチャットフィルタを有効化</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>カスタムパーティ募集フィルタを有効化</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>プライベート募集にフィルタを適用</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>フィルタする正規表現</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} 設定</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>フィルタするキーワード</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>Enable custom chat filters</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>Apply filters to private Party Finder listings</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>Enable custom Party Finder filters</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>Substrings to filter</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>Regular expressions to filter</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} settings</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,32 +0,0 @@
|
|||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>启用自定义聊天消息过滤</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>启用自定义队员招募过滤</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>应用过滤规则至带密码的招募</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>过滤正则表达式</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} 设置</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>过滤关键词</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,32 +0,0 @@
|
|||
<root>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="EnableCustomChatFilters" xml:space="preserve">
|
||||
<value>啟用客製化聊天消息過濾</value>
|
||||
</data>
|
||||
<data name="EnableCustomPartyFinderFilters" xml:space="preserve">
|
||||
<value>啟用客製化隊員招募過濾</value>
|
||||
</data>
|
||||
<data name="FilterPrivatePfs" xml:space="preserve">
|
||||
<value>應用過濾規則至帶密碼的招募</value>
|
||||
</data>
|
||||
<data name="RegularExpressionsToFilter" xml:space="preserve">
|
||||
<value>過濾正則表達式</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>{0} 設置</value>
|
||||
</data>
|
||||
<data name="SubstringsToFilter" xml:space="preserve">
|
||||
<value>過濾關鍵詞</value>
|
||||
</data>
|
||||
</root>
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Costura>
|
||||
<ExcludeAssemblies>
|
||||
Costura
|
||||
CpuMathNative
|
||||
LdaNative
|
||||
</ExcludeAssemblies>
|
||||
</Costura>
|
||||
</Weavers>
|
|
@ -1,28 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>1.1.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NoSoliciting.Interface\NoSoliciting.Interface.csproj"/>
|
||||
<ProjectReference Include="..\NoSoliciting.Internal.Interface\NoSoliciting.Internal.Interface.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Costura.Fody" Version="4.1.0" PrivateAssets="all"/>
|
||||
<PackageReference Include="Fody" Version="6.5.2" PrivateAssets="all"/>
|
||||
<PackageReference Include="JKang.IpcServiceFramework.Hosting.NamedPipe" Version="3.1.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0"/>
|
||||
<PackageReference Include="Microsoft.ML" Version="1.6.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="costura64\*.dll"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,87 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using JKang.IpcServiceFramework.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.ML;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Internal.Interface;
|
||||
|
||||
namespace NoSoliciting.MessageClassifier {
|
||||
internal static class Program {
|
||||
private static void Main(string[] args) {
|
||||
if (!int.TryParse(args[0], out var gamePid)) {
|
||||
Console.WriteLine("No game PID provided.");
|
||||
return;
|
||||
}
|
||||
|
||||
var gameName = args[1];
|
||||
var pipeId = args[2];
|
||||
|
||||
var host = Host.CreateDefaultBuilder()
|
||||
.ConfigureServices(services => {
|
||||
services.AddSingleton<IClassifier, ClassifierService>();
|
||||
})
|
||||
.ConfigureIpcHost(builder => {
|
||||
builder.AddNamedPipeEndpoint<IClassifier>(options => {
|
||||
options.PipeName = $"NoSoliciting.MessageClassifier-{pipeId}";
|
||||
options.Serializer = new BetterIpcSerialiser();
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
Task.Run(async () => {
|
||||
while (true) {
|
||||
Process process;
|
||||
try {
|
||||
process = Process.GetProcessById(gamePid);
|
||||
} catch (Exception) {
|
||||
await host.StopAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.ProcessName != gameName) {
|
||||
await host.StopAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Delay(5_000);
|
||||
}
|
||||
});
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
|
||||
internal class ClassifierService : IClassifier, IDisposable {
|
||||
private MLContext Context { get; set; } = null!;
|
||||
private ITransformer Model { get; set; } = null!;
|
||||
private DataViewSchema Schema { get; set; } = null!;
|
||||
private PredictionEngine<Data, Prediction>? PredictionEngine { get; set; }
|
||||
|
||||
public void Initialise(byte[] data) {
|
||||
if (this.PredictionEngine != null) {
|
||||
this.PredictionEngine.Dispose();
|
||||
this.PredictionEngine = null;
|
||||
}
|
||||
|
||||
this.Context = new MLContext();
|
||||
this.Context.ComponentCatalog.RegisterAssembly(typeof(Data).Assembly);
|
||||
using var stream = new MemoryStream(data);
|
||||
var model = this.Context.Model.Load(stream, out var schema);
|
||||
this.Model = model;
|
||||
this.Schema = schema;
|
||||
this.PredictionEngine = this.Context.Model.CreatePredictionEngine<Data, Prediction>(this.Model, this.Schema);
|
||||
}
|
||||
|
||||
public string Classify(ushort channel, string message) {
|
||||
return this.PredictionEngine?.Predict(new Data(channel, message))?.Category ?? "UNKNOWN";
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.PredictionEngine?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -11,12 +11,11 @@
|
|||
<PackageReference Include="ConsoleTables" Version="2.4.2"/>
|
||||
<PackageReference Include="CsvHelper" Version="27.1.1"/>
|
||||
<PackageReference Include="Microsoft.ML" Version="1.6.0"/>
|
||||
<PackageReference Include="MimeKitLite" Version="2.13.0"/>
|
||||
<PackageReference Include="MimeKitLite" Version="2.15.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NoSoliciting.Interface\NoSoliciting.Interface.csproj"/>
|
||||
<ProjectReference Include="..\NoSoliciting.Internal.Interface\NoSoliciting.Internal.Interface.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -4,7 +4,6 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using ConsoleTables;
|
||||
using CsvHelper;
|
||||
|
@ -16,7 +15,6 @@ using MimeKit;
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Internal.Interface;
|
||||
|
||||
namespace NoSoliciting.Trainer {
|
||||
internal static class Program {
|
||||
|
|
|
@ -51,6 +51,7 @@ COMMUNITY,0,Join Eorzea Multiverse! An 18+ discord for people looking for friend
|
|||
COMMUNITY,0,Join Eorzea Multiverse! An 18+ discord for people looking for friends or relationships! https://discord.gg/d2nQSgk8j4
|
||||
COMMUNITY,0,Join Eorzea Multiverse! An 18+ discord for people looking for friends or relationships! https://discord.gg/d2nQSgk8j4
|
||||
COMMUNITY,0,Join Eorzea Multiverse! An 18+ discord for people looking for friends or relationships! https://discord.gg/eom
|
||||
COMMUNITY,0,Join Eorzea Multiverse! An 18+ discord for people looking for friends or relationships! https://discord.gg/eom
|
||||
COMMUNITY,0,"Join for an invite to a CWLS for SHB Relics. Clusters+fragments farms for memories, and HW FATE farming for memories. "
|
||||
COMMUNITY,0,"Join Geek-O Guild, an international hub for geek project assistance. Join us on discord and see how we can assist you ! <3"
|
||||
COMMUNITY,0,Join our social group themed after the popular jrpg ***Edge of Eternity!*** Everyone is welcome https://discord.gg/vtAFvNUN
|
||||
|
@ -89,6 +90,7 @@ COMMUNITY,11," Rival Wings (Hidden Gorge) popping FRI & SAT - 5pm PDT/ 8pm EDT!
|
|||
COMMUNITY,11,★★ Want all your hunting notifications relayed to you by a sweetly seductive mooing cow? Join Faloop today and get pings directly from you local spawners and train conductors!★★ https://discord.gg/wQt8yzE
|
||||
COMMUNITY,11,♢ ASCEND ♢ a new social discord server uniting gamers that are sick of the toxic gaming environment and just want to have fun.
|
||||
COMMUNITY,11,♢ ASCEND ♢ a new social discord server uniting gamers that are sick of the toxic gaming environment and just want to have fun.
|
||||
COMMUNITY,11,♢ ASCEND ♢ a new social discord server uniting gamers that are sick of the toxic gaming environment and just want to have fun.
|
||||
COMMUNITY,11,Chocobo Rival Wings! Hidden Gorge event TONIGHT! Come have fun!For more info discord.gg/pvprevival
|
||||
COMMUNITY,11,Discord Link: https://discord.gg/m5keWDcyx8
|
||||
COMMUNITY,11,"Dont Forget everyone, The NogsRealm Discord for FF14 Content with ★NoviceDad★ Live Everyday :), New Players Welcome! https://discord.gg/XZMJN3N"
|
||||
|
@ -107,6 +109,7 @@ COMMUNITY,11,Join us this Saturday at 5 PM PST/8 PM EST for !Earn exclusive rewa
|
|||
COMMUNITY,11,kweh! Chocobo Rival wings!!! Hidden Gorge event starting in 1 hour 8pm est!! ChocoboGlam up and head to Adamantoise Wolves Den! for more info check discord.gg/pvprevival
|
||||
COMMUNITY,11,Negate the hate and spread the love. Let's make long-lasting friendships and connections. Join our new social community today!
|
||||
COMMUNITY,11,Negate the hate and spread the love. Let's make long-lasting friendships and connections. Join our new social community today!
|
||||
COMMUNITY,11,Negate the hate and spread the love. Let's make long-lasting friendships and connections. Join our new social community today!
|
||||
COMMUNITY,11,"PRIDE parade and glam contest Come join us at Limsa Aetheryte Plaze, Lunar Whale, Indigo Whale, SDS Fenrir mounts; Resshi, Fuga and Far Eastern attires; mog and in game emotes, minions and hairstyles—all up for grabs! Hosted by the Gilgamesh discord. Join: https://discord.gg/5Va2N3ydEn"
|
||||
COMMUNITY,11,Rival Wings (Hidden Gorge) popping Friday and Saturday - 5pm PDT/ 8pm EDT! 100 wins = 2 Mech Gorilla Mounts. Beginners welcome! More info: discord.gg/pvprevival
|
||||
COMMUNITY,11,"Sign Ups for BlueMage Trains, Map trains, Aether Trains, Mount Farms, Min-iLvL Runs and alot more!"
|
||||
|
@ -124,6 +127,7 @@ FC,0,[RP FC] Silver Sands Collective ((SSCRP)) || Lala-owned business looking fo
|
|||
FC,0,[RP-FC] Kurone is a morally grey yakuza family. Interested in Eastern themed story-driven crime and combat RP? /tell for info!
|
||||
FC,0,[RP] Recruiting people for a LGBTQ friendly RP FC. Please DM me for details. We are located on Faerie
|
||||
FC,0,"[Siren] Looking for a chill af FC? Well, look no further, we're a growing FC with a med. house and active discord! Send /tell."
|
||||
FC,0,"*Shiva* Need a Free Company? Join us "" Retribution"" ^^ hope to see you there, you can also just send a message"
|
||||
FC,0,#Bardsquad FC is looking for friends to hangout and punch primals with. If you like both pst or join the party :)
|
||||
FC,0,"<<GRIND>> is recruiting! If you're looking for a med-sized social active FC, then we would love to meet you! /tell for more info?"
|
||||
FC,0,"<B&B> Always recruiting, new or vets, LGBTQ+ welcome! We run all kinds of content! Send a tell or DM @Halftime#0001 on Discord"
|
||||
|
@ -159,6 +163,9 @@ FC,0,"HAIL - Active, Social & Mature Company recruiting members! Our FC touches
|
|||
FC,0,Hallow Wolves <Fang> is looking for members to join the FC. We have a house and a discord~
|
||||
FC,0,Hey you want FC? We chill FC. We have house and those cool buffs. Send me a tell
|
||||
FC,0,"Hey, you!Lookin' for an active FC?Join us on CETRA! New?We got mentors! You raid?Join us!All are welcome.Apply or dm me to join"
|
||||
FC,0,"Hop in weirdos! Join TwoPaths FC! Housing, Discord, Etc, Etc, blah, blah, sellusursoul,blah,blah Contact @ https://discord.gg/h6U6rBjJ"
|
||||
FC,0,"Hop in weirdos! Join TwoPaths FC! Housing, Discord, Etc, Etc, blah, blah, sellusursoul,blah,blah Contact @ https://discord.gg/h6U6rBjJ"
|
||||
FC,0,"Hop in weirdos! Join TwoPaths FC! Housing, Discord, Etc, Etc, blah, blah, sellusursoul,blah,blah Contact @ https://discord.gg/h6U6rBjJ"
|
||||
FC,0,"Interested in a fun, food-loving and LGBT+ friendly FC? Meshitero <-w-> is looking for friends to enjoy content with! /tell or join"
|
||||
FC,0,"Join Dairy Queen! A new FC looking for helpful, friendly players of all levels to join our community! Join for more info! ^^"
|
||||
FC,0,Journey's End seeking New Members! R30 FC looking for friendly individuals to enjoy the game with. join or PST for details.
|
||||
|
@ -331,8 +338,11 @@ FLUFF,0,(/~5th Annual Balmung Lalafell Parade! ~\) -- discord.gg/lalafell TOMOR
|
|||
FLUFF,0,(/~5th Annual Balmung Lalafell Parade! ~\) -- discord.gg/lalafell TOMORROW 4p PT | 7p ET -- Ul'dah - All races/worlds are welcome.
|
||||
FLUFF,0,(/~5th Annual Balmung Lalafell Parade! ~\) -- discord.gg/lalafell TOMORROW 4p PT | 7p ET -- Ul'dah - All races/worlds are welcome.
|
||||
FLUFF,0,(/~5th Annual Balmung Lalafell Parade! ~\) -- discord.gg/lalafell TOMORROW 4p PT | 7p ET -- Ul'dah - All races/worlds are welcome.
|
||||
FLUFF,0,[ART] Drawing Chibis of your characters! Send tell for prices and location to meet at. https://lemon-bun-shoppe.carrd.co/
|
||||
FLUFF,0,"[FFXIV ART] Drawing some Chibi Ysh'tola, come along and watch her come to life @ twitch.tv/stressedkwi"
|
||||
FLUFF,0,"[FREE ART] Heros of Eorzea | twitch.tv/stressedkiwi | Come chill in Limsa! "
|
||||
FLUFF,0,"{ART} Doing ffxiv commissions! | Headshots, Half-Bodies, Full Bodies, Emotes, & Chibis! | disc: Ari#4985 | https://ari-art.carrd.co/"
|
||||
FLUFF,0,{Art} I do character portraits :D please let me know what you think! https://tacotakeoart.carrd.co/ <3 <3
|
||||
FLUFF,0,*insert JoJo reference here* -> https://discord.gg/bSYaY8WaY3
|
||||
FLUFF,0,<3 LF a wife/gf /pst or dm GothicKitten#6969 on discord <3
|
||||
FLUFF,0,<3 LF a wife/gf for longterm romance relationship and EB /pst or dm Izuka Katsumi#5168 on discord <3
|
||||
|
@ -386,6 +396,7 @@ FLUFF,0,Duo doodling! Get your portrait drawn by Tori and Aphi! https://picarto.
|
|||
FLUFF,0,"Fashion show and glamour contest tonight, 7 PM EST @ Dia Goblet 6th Ward! Come see our collections and compete for prizes!"
|
||||
FLUFF,0,"game's dying. content is trash. reject 14, embrace league."
|
||||
FLUFF,0,Get custom pictures of your character! Follow link for detail! https://rsppictures.carrd.co/
|
||||
FLUFF,0,Giveaway 100000 Gils everyday @ twitch.tv/ifashu (FR/EN)(send tell here or twitch if any questions!)
|
||||
FLUFF,0,Handing out FREE fashion report gear at the Gilgamesh Gold Saucer! Just need a level 40 DoW!
|
||||
FLUFF,0,Handing out FREE fashion report gear by the Main Gold Saucer Aetheryte! Come earn an easy 60K MGP!
|
||||
FLUFF,0,"Hello friends, I'm currently looking for anime fans to interview for a capstone project! If you have any interest, join and chat!"
|
||||
|
@ -433,7 +444,10 @@ FLUFF,0,"No matter how you may be feeling right now just remember, you DO matter
|
|||
FLUFF,0,"Octopies, get them while they're hot; IC-DH :D"
|
||||
FLUFF,0,"Offering my Artist skill for Commissions, Portrait, Lineart and more. join me for more info. Not Feet pics but close. "
|
||||
FLUFF,0,"Pass my Jump Puzzle and write in my message book to win 300k gil! 0/3 Claimed [Levi, Lavender Bed Ward 3 Wing 1 Apt #82]"
|
||||
FLUFF,0,"PLURadise is BACK! 1 Mil Gil Summer Glam Contest @ Jenova, Mist, W15 P45! Party with Daddy LIVE! @ twitch.tv/DasGud "
|
||||
FLUFF,0,pm me for discord feet pic
|
||||
FLUFF,0,POP-UP RIOT @ 8pm est with DJs Aerial and Mayhem. DIA GOB| W18 | P1 Twitch: https://twitch.tv/aerialmayhem outside ROCK!
|
||||
FLUFF,0,POP-UP RIOT @ 8pm est with DJs Aerial and Mayhem. DIA GOB| W18 | P1 Twitch: https://twitch.tv/aerialmayhem outside ROCK!
|
||||
FLUFF,0,pro photographer for everything( also a taxi for people who cant fly in certain areas)
|
||||
FLUFF,0,"soft gay Xaela boy wishing everyone a fantastic morning! also, friendly reminder to drink water if you haven't yet! :3c [afk] "
|
||||
FLUFF,0,"super bored lvling crafting, join and talk c: "
|
||||
|
@ -448,6 +462,7 @@ FLUFF,0,will do your taxes for casual top
|
|||
FLUFF,0,Would you like your picture taken? Join up and lets see what we can do together? Just doin it for fun.
|
||||
FLUFF,0,you know who you are
|
||||
FLUFF,0, HOUSE DECORATING SERVICE I want to build your dream space! My furniture or yours! Cost/Examples: zekhdesigns.carrd.co
|
||||
FLUFF,11,★Drawing Event- NOW!★ Hi! Do you fancy character portraits? Lively banter? A healthy dose of utter nonsense?? Then why not join us on some escapades?!
|
||||
FLUFF,11,★Drawing Event- Starting Soon!★ Hi! Do you fancy character portraits? Lively banter? A healthy dose of utter nonsense?? Then why not join us on some escapades?!
|
||||
FLUFF,11,★Drawing Event- Starting Soon!★ Hi! Do you fancy character portraits? Lively banter? A healthy dose of utter nonsense?? Then why not join us on some escapades?!
|
||||
FLUFF,11,ART DRAWING EVENT: SOON! Like art? Lively Conversation? Sea Pickles?! Come hang out while Phaegan Agana DRAWS CHARACTER PORTRAITS 4PM PST @twith.tv/wanderpus
|
||||
|
@ -457,6 +472,8 @@ FLUFF,11,"Greetings and salutations new adventurers! Welcome to Eorzea! I invi
|
|||
FLUFF,11,"Hello beautiful people, can you help us with a follow on twitch.tv/hefty_gaming? We are a group of new players that want to build a friendly community, everyone is welcome. If you have the time join the conversation! (discord on twitch bio). Thank you and have fun!"
|
||||
FLUFF,11,"Hello beautiful people, can you help us with a follow on twitch.tv/hefty_gaming? We are a group of new players that want to build a friendly community, everyone is welcome. If you have the time join the conversation! (discord on twitch bio). Thank you and have fun!"
|
||||
FLUFF,11,"Hello beautiful people, can you help us with a follow on twitch.tv/hefty_gaming? We are a group of new players that want to build a friendly community, everyone is welcome. If you have the time join the conversation!. Thank you and have fun! (discord on twitch bio)"
|
||||
FLUFF,11,"Let us keep you creative company while you grind that content straight into oblivion! Our signature blend of art, comedy, & stupidity- live on twitch.tv/wanderpus"
|
||||
FLUFF,11,Tune into this amazing DJ talent! He rocks my socks off. Wait I am not wearing any. Hehe. https://www.twitch.tv/blaugrana8
|
||||
FLUFF,11, aka // Tune into his stream - https://www.twitch.tv/djmannmade
|
||||
NORMAL,0," blue mage log for the HW 24 man! We'll be doing Void Ark -> Weeping City -> Dun Scath, come for fun!"
|
||||
NORMAL,0," blue mage log for the HW 24 man! We'll be doing Void Ark -> Weeping City -> Dun Scath, come for fun!"
|
||||
|
@ -522,6 +539,7 @@ NORMAL,0,"51-151+ Have around 80/80 aetherpool, artifact grinding if possible"
|
|||
NORMAL,0,"51-200, 80+ gear"
|
||||
NORMAL,0,"5th week of this shit... P2 after basic onwards, Chinese Lions"
|
||||
NORMAL,0,"6pm(EST) boat, going for sea dragoon title"
|
||||
NORMAL,0,70+ let get this mount
|
||||
NORMAL,0,"8 runs / 8 mounts, stay for all please"
|
||||
NORMAL,0,9 thru 12 pf strat 2 chest
|
||||
NORMAL,0,"9-10 weekly 2 chest, happy, akito vg2"
|
||||
|
@ -623,6 +641,7 @@ NORMAL,0,Doing Art commission of your charactet with good price! more info add m
|
|||
NORMAL,0,doing e9 to 11 usual pug strats 2 chest
|
||||
NORMAL,0,doing final floor of each savage fight to unlock ultimates
|
||||
NORMAL,0,"DR speed running strats, if you join plz let me know what actions/essences youre bringing so theres no overlap thanks!"
|
||||
NORMAL,0,DR Speedruns! Work out rends/dervish/flarestar/banish/eldercure at 20/24
|
||||
NORMAL,0,draggo farm
|
||||
NORMAL,0,"Draggo Farm, T/H CW partners (CW SMN) , DPS towers, NIN uptime, N= 1-4 E= 2-5 S= 3-7 W=4-8 BLM"
|
||||
NORMAL,0,DRN Spams Brought To You Via CEM!
|
||||
|
@ -666,6 +685,7 @@ NORMAL,0,farming dragons / post macro if u want / DONT JOIN IF U WANT TO LEAVE C
|
|||
NORMAL,0,Farming Heavens' Ward helm Fragments
|
||||
NORMAL,0,Farming Heavens' Ward helm Fragments
|
||||
NORMAL,0,"Farming this for gear, but I'm also willing to teach newcomers."
|
||||
NORMAL,0,"Farmo Party, Standard Stuff"
|
||||
NORMAL,0,fast dragon farm bring macro and markers please !
|
||||
NORMAL,0,Fat fuk needs to drop a mount for us
|
||||
NORMAL,0,"FFA - Zonureskin maps - Buy 2 "
|
||||
|
@ -744,6 +764,7 @@ NORMAL,0,Just looking for someone to enjoy the game with. Can use me for easy qu
|
|||
NORMAL,0,Just looking to make some friends. Kinda lonely. Come to my house if you want. Mateus Goblet ward 22 plot 48
|
||||
NORMAL,0,Just wanna do this again for fun
|
||||
NORMAL,0,Just want a few weapon greed only look join if u like to cheese it ty
|
||||
NORMAL,0,"Just want Kefka minion "
|
||||
NORMAL,0,"kaine join my party :) "
|
||||
NORMAL,0,Kill/Log/Fun Party - Only P1- Dwayne/Chinese - Know the fight - Free ERP
|
||||
NORMAL,0,"know the fight, if someone falls off, laugh at them!"
|
||||
|
@ -867,10 +888,12 @@ NORMAL,0,Practice from Seeds and onward. Help appreciated o:
|
|||
NORMAL,0,practice from sundered be patient m1 taken bring macro and markers braindead blue
|
||||
NORMAL,0,"practice from Voidgate. Helpers welcome. discord ok, only for german (if you want make callouts :D) R2/M2 taken"
|
||||
NORMAL,0,"Practice runs, hopefully we can clear. Discord is available, veteran welcomed. https://discord.gg/E8uN3xp"
|
||||
NORMAL,0,"Practis 4 Friend, we play 1357"
|
||||
NORMAL,0,Practise platform and onward!
|
||||
NORMAL,0,Practise platform and onward!
|
||||
NORMAL,0,"Premade queue-sync. Tomorrow Friday, 11pm EST (13:00 JST, Saturday). gather Jenova Wolves' Den Pier (+cwls,discord)"
|
||||
NORMAL,0,Prog for 1. Happy brambles
|
||||
NORMAL,0,prog for friends they're new to it.
|
||||
NORMAL,0,"Prog group! Have gotten to 10% in the 2nd phase, still shakey w/ a few mechs tho. :) vets appreciated but can take some newish ppl."
|
||||
NORMAL,0,"Prog group. 4:4, Saw phase 2 enrage"
|
||||
NORMAL,0,Prog to Clear | Pairs | Vets welcome | Helping FC mates
|
||||
|
@ -2320,6 +2343,7 @@ NORMAL,11,still waiting for peggy. i'm out in the open
|
|||
NORMAL,11,stop being lazy and be a good FC person
|
||||
NORMAL,11,Take my class on how to not be a nice guy.
|
||||
NORMAL,11,"TBH, new races? Nah. Wasted. Just expand on current races; New hair, new faces, new tail styles, new features"
|
||||
NORMAL,11,teamcraft should have the info
|
||||
NORMAL,11,tell me you're a tumblr dom without telling me you're a tumblr dom
|
||||
NORMAL,11,Tell the culinarian assholes they're -wrong- and to try again.
|
||||
NORMAL,11,that in itself is rulebreaking
|
||||
|
@ -2504,6 +2528,12 @@ RMT_C,0,"[LALAKUZA] Eden's Promise Savage i530/535 loot, BLU Morbol, UWU,UCOB,TE
|
|||
RMT_C,0,"[LALAKUZA] Eden's Promise Savage, BLU Morbol mount, UWU,UCOB,TEA Ultimate, Raids, Trials. Discord: Lalakuza#1157"
|
||||
RMT_C,0,"[LALAKUZA] Eden's Savage, BLU Morbol Mount, UWU,UCOB,TEA Ultimate, PoTD/HoH Titles, Trials. Disc: Lalakuza#1157"
|
||||
RMT_C,0,"[LALAKUZA] Eden's Savage, BLU Morbol Mount, UWU,UCOB,TEA Ultimate, PoTD/HoH Titles, Trials. Disc: Lalakuza#1157"
|
||||
RMT_C,0,"[No Wallets Allowed] - NO RMT, GIL ONLY! Offering Savage, Trials, BLU, Mentoring & much more! me for more info!"
|
||||
RMT_C,0,"[No Wallets Allowed] - NO RMT/Gil-Only! Offering Savage, Raids, Trials, BLU & much more! for more info"
|
||||
RMT_C,0,"[No Wallets Allowed] - NO RMT/Gil-Only! Offering Savage, Raids, Trials, BLU & much more! for more info"
|
||||
RMT_C,0,"[No Wallets Allowed] - NO RMT/Gil-Only! Offering Savage, Raids, Trials, BLU & much more! for more info"
|
||||
RMT_C,0,"[No Wallets Allowed] - NO RMT/Gil-Only! Offering Savage, Raids, Trials, BLU & much more! for more info"
|
||||
RMT_C,0,"[No Wallets allowed] Selling Savage, Mounts, Blu skills, Mentoring [Only Gil no RMT] DM me for more info!"
|
||||
RMT_C,0,[Rage] E9S-E12S - Ucob/Uwu/Tea - Primals - Mounts - Blu - Coaching - Potd/Hoh - Delubrum Reginae and more! rixia#8353
|
||||
RMT_C,0,"[Sakura] Eden 9-12, Ultimates, POTD/HOH, Primals, DR Savage, Mentoring, FFlogs, Blu Mage etc. Minerva#1412"
|
||||
RMT_C,0,"[Tranquility FC] Check out our help runs! Savages, Ultimates, POTD/HOH, Titles, Primals, Cerberus, Mentoring. Fierytail#0001 "
|
||||
|
@ -2682,6 +2712,7 @@ RMT_C,0,"9-12S 48h service less than <$150, Ultimate Raid less than <$120, also
|
|||
RMT_C,0,"9-12S 48h service less than <$150, Ultimate Raid less than <$120, also titles and mounts, contact Bearseal1010#2401"
|
||||
RMT_C,0,"AnyPvE,{Mount}{E9-12s}{TheNecromancer}{AstropeWhistle}{UWU}{UCOB}{TEA}welcome to consult-Discord:Bean curd#0049"
|
||||
RMT_C,0,Available NOW! GIL ONLY! Selling All HW/SB/ShB Primals/ O4-8-12S/E4S >> Send a tell or visit http://rollraider.carrd.co/ <<
|
||||
RMT_C,0,Chocorpokkur Mounts available. Missed the event? Discord for info BunBun#0001
|
||||
RMT_C,0,"Eden 9-12, Ultimates, POTD/HOH, Primals, DR Savage, Mentoring, FFlogs, Blu Mage etc. Here: Minerva#1412"
|
||||
RMT_C,0,Fast and easy help with ≪ ❶ Savage 一 ❷ Ultimates 一 ❸ Mounts ❹ Blu ≫ Discord → ashlar#6021
|
||||
RMT_C,0,GIL ONLY | All Savage | All Mounts | All Extremes | All Ultimates >>ffyakuza #3893 or https://ffyakuza.carrd.co/<<
|
||||
|
@ -2829,6 +2860,7 @@ RP,0,"[18+] ♥ Otter Springs Spa & Resort ♥ CURRENTLY HIRING, NO EXP REQUIRED
|
|||
RP,0,"[18+] ATTIKA is now open! Raffle tonight for a chance to win cool prizes! Dancing, Drinks, Food! Sarg; Shiro W6 P28. 10pm EST♥"
|
||||
RP,0,[18+] CLUB AFTERLIFE is open! Come through our gates and drink the night away! [10PM-2AM EST] [Jenova/Shirogane/W14/P16]
|
||||
RP,0,[18+] Gilga LB 22/58. 4th floor penthouse. Elevator is on the right as you walk in the door. Bring friends and a bathing suit.
|
||||
RP,0,"[18+] I guess this is as good a time as any to say my parents aren't home. LB 22/58, bring your friends!!!"
|
||||
RP,0,"[18+] LuXe Nightclub - Hiring Entertainers, Hosts, Cocktail Staff, Bartenders, Dealers, Security - https://discord.gg/fpx4DFcbdt"
|
||||
RP,0,[18+] Please join us tonight at 9PM EST LB P33W22 Yorha theme For more info: https://milkym.carrd.co/
|
||||
RP,0,[18+] Scarlet Night is open! Live Music w/ Two imaginary Girls/Baths/Bar/Trivia! Adamantoise Shirogane W16 P7
|
||||
|
@ -3026,6 +3058,7 @@ RP,0,<Nyan Diner> Coeurl Mist Ward 17 Plot 52! Please join us! Open from 6pm-9p
|
|||
RP,0,"<SFW-RP> The Winter Market is here! Come for a night of fun, festivity and Starlight cheer! Observatorium in CCH, Gilgamesh!"
|
||||
RP,0,"| - Hangin out in the Hotsprings, talkin n' stuff. RP's cool also - | - Exodus - Kugane - Bokaisen Hot Springs, West of Bokairo Inn - |"
|
||||
RP,0,~♡NOW OPEN♡~ The Ruby Lounge – GIVEAWAYS - Dancers and Drinks - Malboro Gob W3P30 - therubylounge.carrd.co/
|
||||
RP,0,◇ Limitless Chaos Club ◇ Tonight NieR party - 10pm-12am CEST | Spriggan - Goblet - w7 p30 | Dj Live + Gil M Giveaway + games +...
|
||||
RP,0,"● The Pearl ● a Victorian Brothel opening Tuesday! Applications closing tonight, get yours in now! [ thepearlxiv.carrd.co ]"
|
||||
RP,0,"★ 18:00 ST ★ SUGAR & SPICE ★ GLAMS GAMBLING DEBAUCHERY ★ HEAVENLY HELL ★ Spriggan, The Goblet (18th Ward, Plot 13) ★"
|
||||
RP,0,"★ TODAY 19:00 ST ★ Kitty Kat X-Mas ★ Open Party at 'The Catwalk' ★ Spriggan, The Goblet - 19th Ward, Plot 35 ★"
|
||||
|
@ -3061,6 +3094,7 @@ RP,0,"♥Booty House♥ Cactuar / LB w3,p35 (South sub.) ♥ Fridays 6pm PST ♥
|
|||
RP,0,"♥Booty House♥ Cactuar / LB w3,p35 (South sub.) ♥ Fridays 6pm PST ♥ MAGICAL GIRLS ♥ https://discord.gg/XzjBxbF"
|
||||
RP,0,♥Daffodil Hill Maid Cafe is open from 10PM-1AM EST!♥ We're a fun SFW environment! SARG. LAV BEDS W:14 P:51! uwu
|
||||
RP,0,♥Faeries' Caress is open! ♥ Come enjoy a chill sunday with us and share art in our stage! ♥ Adamantoise - L B - W15 - Plot 12
|
||||
RP,0,♥Soft Miqo School Wants To Do Things You Ask In Exchange For Gil! Dm Your Request♥
|
||||
RP,0,"♥THE WILLOW IS OPEN @10pm EST ♥DOMS & SUBS NIGHT♥ LIVE Performers, Drinks, Dancing, & MORE♥ Siren Lav Beds W21 P6"
|
||||
RP,0,"♥THE WILLOW IS OPEN♥ 7pm-12am EST♥ Siren~Lav Beds~W21 P6♥ Join us for drinks, dancing, and more fun!♥"
|
||||
RP,0,♪ THE BARDS' GUILD ♪ CommunityCome vibe and learn with other bard //Concert Hall// Gigs♪ https://discord.gg/rhZ2r9m2f8
|
||||
|
@ -3127,6 +3161,7 @@ RP,0,Craving Pizza? Pogazu Pizza is open! Dine in @ Ada Mist W23 P21 / Join Part
|
|||
RP,0,Crescents Keep All Saints Date Auction and Costume Party! Over 1mil in prizes! Mist Ward 14 Plot 34 7-10 EST
|
||||
RP,0,Crystal Order Academy is looking for students! We are an RP community! if you want to know more than join or send a tell!
|
||||
RP,0,cute bun egirl looking for eboys uwu
|
||||
RP,0,cute lala maid doing anything for gil
|
||||
RP,0,cute miqo girl down for anything uwu
|
||||
RP,0,Darkstalkers is a voidhunter based rp FC looking for members! Why not give into your Edge and join up with the us!
|
||||
RP,0,DEVIL MAY CARE / 9PM EST Grand Reopening / Bask in the Devilish Decadence / Cact / Gob / W6 P 12 / dmcbuilding.carrd.co
|
||||
|
@ -3327,6 +3362,7 @@ RP,0,"Springtime Surprise! Adamantoise, P28, W15 Lavender Beds - 4/24 8PM CST Sp
|
|||
RP,0,"SQUAD RAGER! Into the woods, A Fairy tale RAGER! 11pm EST, Zalera, South Shroud Quarry Mill Aetheryte! Then travel to 33-23. "
|
||||
RP,0,"STARSTRUCK plays 9 NOW ♥ faerie, Goblet-W6-P13. Come see your fravorite idol group tonight at The Sirens Abyss."
|
||||
RP,0,Sugar Sugar Hime Lounge is hosting a winter raffle! Drinks are on the house <3 Faerie - Goblet - W14 P35 discord.gg/HRpZ54m
|
||||
RP,0,sweeping for tips in the hyperion gridania markets. god bless.
|
||||
RP,0,"Tako Train ""Maid"" Cafe presents Winter Wonderland ft. REAL ELSA AND AMBER WOW Cact. LavBeds W9 P27 ~ takotraincafe.carrd.co"
|
||||
RP,0,Tea Thyme Cafe open W9 P13 Mist Ada. Stop on by and get some Love packs from tea thyme. Open stage mic all night ♥♥♥♥♥
|
||||
RP,0,"The Blooming Rose is open! Come by for drinks, dancing, and chill vibes. Siren LB W7P21, 7pm to 10pm EST"
|
||||
|
@ -3937,6 +3973,7 @@ TRADE,0,5 min photoshoot 20k for you or a group. i do wedding pics and friend pi
|
|||
TRADE,0,"5 min photoshoot 30,000gil for you and or a friend. custom work. freelancer. send me a message."
|
||||
TRADE,0,Any skybuilders' scrip mount for 750k
|
||||
TRADE,0,"ATTENTION FC LEADERS ! Selling SCHEMATICS BOARD MATS of all types-Florist house glam kit=2.5mil, c.lumber(120k) & p.clay(100k"
|
||||
TRADE,0,"B > 1or 2 Warden of the Seven Hue =D come on in and chat "
|
||||
TRADE,0,Benben Minion for sale. Join with offers.
|
||||
TRADE,0,Blue Moon Phasmascape! Selling for 8.5m OBO Pls join!! Save us both MB taxes!
|
||||
TRADE,0,"Bored and Broke Bun selling conversations, 500k for 30 minutes. I got bills man."
|
||||
|
@ -4034,6 +4071,7 @@ TRADE,0,Master (melding) Omni-crafter! All recipies acquired! Discounts availabl
|
|||
TRADE,0,"Need a few Pelagic Clay? Need a lot? I've got 35 to sell! 200k for each, send a tell or join! Also have a couple Coco Lumbrers."
|
||||
TRADE,0,Need a mansion in Faerie? I have a large mansion in Faerie. Discord - Wulfen#8144
|
||||
TRADE,0,"Need bulk materials before expansion? Hire us! Fast, professional and cheaper than the MB by 20% or more! Dodo Gathering "
|
||||
TRADE,0,Need help with decoration your house? Moonlight decor can help! I love doing all builds. <3 https://discord.gg/Qdh4su7U5M
|
||||
TRADE,0,Night Steel Pegasus 14M. Skip the MB tax. Ride a cool horse that makes people think you did Palace of the Dead real good!
|
||||
TRADE,0,Offering Gather Service - 30% Less than MB per item
|
||||
TRADE,0,"Omni Crafter for Hire. If you have the mats , will craft for less than MB prices. Negotiations applicable. Can craft Frontier etc."
|
||||
|
@ -4185,6 +4223,7 @@ TRADE,0,"WTB small, pst me or pop in party. <3 "
|
|||
TRADE,0,WTB stacks of Stormsap 500k/stack
|
||||
TRADE,0,WTB Workshop Mats
|
||||
TRADE,0,"WTS - Incitatus Whistle - Cheapest on Aether! 11,000,000 - avoid marketboard and tax for us both!"
|
||||
TRADE,0,WTS CHEAP gathering service BTN/MIN. Cheaper prices for bulk buyers. Send /tell for more info dc: R$chB$tch#3169
|
||||
TRADE,0,WTS Copycat Bulb (Minion) Cheaper than MB /tell if interested
|
||||
TRADE,0,"WTS Eastern Cherry Tree, cheaper than MB - Jenova only - PST or Join"
|
||||
TRADE,0,WTS FC with S plot in LB (plot 29)
|
||||
|
|
|
|
@ -11,12 +11,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Interface", "N
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Trainer", "NoSoliciting.Trainer\NoSoliciting.Trainer.csproj", "{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.MessageClassifier", "NoSoliciting.MessageClassifier\NoSoliciting.MessageClassifier.csproj", "{16689469-6A74-4197-818A-EA44697BD815}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Internal.Interface", "NoSoliciting.Internal.Interface\NoSoliciting.Internal.Interface.csproj", "{742F1B3F-030F-4886-B05D-0D41D4DDA8FD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoSoliciting.Lite", "NoSoliciting.Lite\NoSoliciting.Lite.csproj", "{46679548-E204-453B-AAAC-F31342071E03}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -35,18 +29,6 @@ Global
|
|||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D774127-F7A9-4B6D-AB2F-3AAF80D15586}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{16689469-6A74-4197-818A-EA44697BD815}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{16689469-6A74-4197-818A-EA44697BD815}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{16689469-6A74-4197-818A-EA44697BD815}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{16689469-6A74-4197-818A-EA44697BD815}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{742F1B3F-030F-4886-B05D-0D41D4DDA8FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{742F1B3F-030F-4886-B05D-0D41D4DDA8FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{742F1B3F-030F-4886-B05D-0D41D4DDA8FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{742F1B3F-030F-4886-B05D-0D41D4DDA8FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{46679548-E204-453B-AAAC-F31342071E03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{46679548-E204-453B-AAAC-F31342071E03}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{46679548-E204-453B-AAAC-F31342071E03}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{46679548-E204-453B-AAAC-F31342071E03}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -8,23 +8,23 @@ namespace NoSoliciting {
|
|||
internal Commands(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Plugin.Interface.CommandManager.AddHandler("/prmt", new CommandInfo(this.OnCommand) {
|
||||
this.Plugin.CommandManager.AddHandler("/prmt", new CommandInfo(this.OnCommand) {
|
||||
HelpMessage = "Opens the NoSoliciting configuration (deprecated)",
|
||||
ShowInHelp = false,
|
||||
});
|
||||
this.Plugin.Interface.CommandManager.AddHandler("/nosol", new CommandInfo(this.OnCommand) {
|
||||
this.Plugin.CommandManager.AddHandler("/nosol", new CommandInfo(this.OnCommand) {
|
||||
HelpMessage = "Opens the NoSoliciting configuration",
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.CommandManager.RemoveHandler("/nosol");
|
||||
this.Plugin.Interface.CommandManager.RemoveHandler("/prmt");
|
||||
this.Plugin.CommandManager.RemoveHandler("/nosol");
|
||||
this.Plugin.CommandManager.RemoveHandler("/prmt");
|
||||
}
|
||||
|
||||
private void OnCommand(string command, string args) {
|
||||
if (command == "/prmt") {
|
||||
this.Plugin.Interface.Framework.Gui.Chat.PrintError($"[{this.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") {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Resources;
|
||||
using XivCommon.Functions.ContextMenu;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
OutputPath="$(OutputPath)"
|
||||
AssemblyName="$(AssemblyName)"
|
||||
VersionComponents="3"
|
||||
MakeZip="true"
|
||||
Include="NoSoliciting.json;NoSoliciting.dll;NoSoliciting.pdb"/>
|
||||
MakeZip="true"/>
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using Dalamud.Game.Internal.Gui;
|
||||
using Dalamud.Game.Internal.Gui.Structs;
|
||||
using System;
|
||||
using Dalamud.Game.Gui.PartyFinder.Types;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Logging;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Ml;
|
||||
|
||||
|
@ -48,8 +47,8 @@ namespace NoSoliciting {
|
|||
public Filter(Plugin plugin) {
|
||||
this.Plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
|
||||
|
||||
this.Plugin.Interface.Framework.Gui.Chat.OnCheckMessageHandled += this.OnChat;
|
||||
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing += this.OnListing;
|
||||
this.Plugin.ChatGui.CheckMessageHandled += this.OnChat;
|
||||
this.Plugin.PartyFinderGui.ReceiveListing += this.OnListing;
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing) {
|
||||
|
@ -58,8 +57,8 @@ namespace NoSoliciting {
|
|||
}
|
||||
|
||||
if (disposing) {
|
||||
this.Plugin.Interface.Framework.Gui.Chat.OnCheckMessageHandled -= this.OnChat;
|
||||
this.Plugin.Interface.Framework.Gui.PartyFinder.ReceiveListing -= this.OnListing;
|
||||
this.Plugin.ChatGui.CheckMessageHandled -= this.OnChat;
|
||||
this.Plugin.PartyFinderGui.ReceiveListing -= this.OnListing;
|
||||
}
|
||||
|
||||
this._disposedValue = true;
|
||||
|
@ -184,7 +183,7 @@ namespace NoSoliciting {
|
|||
}
|
||||
|
||||
// step 1. check if pf has an item level that's too high
|
||||
if (this.Plugin.Config.FilterHugeItemLevelPFs && listing.MinimumItemLevel > FilterUtil.MaxItemLevelAttainable(this.Plugin.Interface.Data)) {
|
||||
if (this.Plugin.Config.FilterHugeItemLevelPFs && listing.MinimumItemLevel > FilterUtil.MaxItemLevelAttainable(this.Plugin.DataManager)) {
|
||||
return (null, "ilvl");
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace NoSoliciting {
|
|||
|
||||
var ilvls = new Dictionary<Slot, int>();
|
||||
|
||||
foreach (var item in data.GetExcelSheet<Item>()) {
|
||||
foreach (var item in data.GetExcelSheet<Item>()!) {
|
||||
var slot = SlotFromItem(item);
|
||||
if (slot == null) {
|
||||
continue;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Resourcer/>
|
||||
<ResourcesMerge/>
|
||||
</Weavers>
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Target Name="ILRepacker" AfterTargets="Build">
|
||||
|
||||
<ItemGroup>
|
||||
<InputAssemblies Include="$(OutputPath)\NoSoliciting.dll"/>
|
||||
<InputAssemblies Include="$(OutputPath)\NoSoliciting.Interface.dll"/>
|
||||
<InputAssemblies Include="$(OutputPath)\J*.dll"/>
|
||||
<InputAssemblies Include="$(OutputPath)\M*.dll"/>
|
||||
<InputAssemblies Include="$(OutputPath)\S*.dll"/>
|
||||
<InputAssemblies Include="$(OutputPath)\Y*.dll"/>
|
||||
<InputAssemblies Include="$(OutputPath)\XivCommon.dll"/>
|
||||
</ItemGroup>
|
||||
<ILRepack
|
||||
Parallel="true"
|
||||
Internalize="true"
|
||||
InputAssemblies="@(InputAssemblies)"
|
||||
LibraryPath="$(OutputPath);$(AppData)\XIVLauncher\addon\Hooks\dev"
|
||||
TargetKind="Dll"
|
||||
OutputFile="$(OutputPath)\$(AssemblyName).dll"
|
||||
/>
|
||||
|
||||
</Target>
|
||||
</Project>
|
|
@ -13,11 +13,11 @@ namespace NoSoliciting.Interface {
|
|||
this.Settings = new Settings(plugin, this);
|
||||
this.Report = new Report(plugin);
|
||||
|
||||
this.Plugin.Interface.UiBuilder.OnBuildUi += this.Draw;
|
||||
this.Plugin.Interface.UiBuilder.Draw += this.Draw;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.UiBuilder.OnBuildUi -= this.Draw;
|
||||
this.Plugin.Interface.UiBuilder.Draw -= this.Draw;
|
||||
this.Settings.Dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Numerics;
|
|||
using System.Threading.Tasks;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Plugin;
|
||||
using Dalamud.Logging;
|
||||
using ImGuiNET;
|
||||
using NoSoliciting.Ml;
|
||||
using NoSoliciting.Resources;
|
||||
|
@ -68,6 +68,7 @@ namespace NoSoliciting.Interface {
|
|||
|
||||
var windowTitle = string.Format(Language.Reporting, this.Plugin.Name);
|
||||
if (!ImGui.Begin($"{windowTitle}###NoSoliciting reporting", ref this._showReporting)) {
|
||||
ImGui.End();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -125,7 +126,7 @@ namespace NoSoliciting.Interface {
|
|||
.Select(payload => payload.Text)
|
||||
.FirstOrDefault() ?? "";
|
||||
|
||||
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.ChatType.Name(this.Plugin.Interface.Data), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
|
||||
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.ChatType.Name(this.Plugin.DataManager), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
|
||||
ImGui.OpenPopup($"###modal-message-{message.Id}");
|
||||
}
|
||||
|
||||
|
@ -320,12 +321,12 @@ namespace NoSoliciting.Interface {
|
|||
switch (status) {
|
||||
case ReportStatus.Successful: {
|
||||
var msg = Language.ReportToastSuccess;
|
||||
this.Plugin.Interface.Framework.Gui.Toast.ShowNormal(string.Format(msg, message.Sender));
|
||||
this.Plugin.ToastGui.ShowNormal(string.Format(msg, message.Sender));
|
||||
break;
|
||||
}
|
||||
case ReportStatus.Failure: {
|
||||
var msg = Language.ReportToastFailure;
|
||||
this.Plugin.Interface.Framework.Gui.Toast.ShowError(string.Format(msg, message.Sender));
|
||||
this.Plugin.ToastGui.ShowError(string.Format(msg, message.Sender));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,14 +24,14 @@ namespace NoSoliciting.Interface {
|
|||
this.Plugin = plugin;
|
||||
this.Ui = ui;
|
||||
|
||||
this.Plugin.Interface.UiBuilder.OnOpenConfigUi += this.Open;
|
||||
this.Plugin.Interface.UiBuilder.OpenConfigUi += this.Open;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Interface.UiBuilder.OnOpenConfigUi -= this.Open;
|
||||
this.Plugin.Interface.UiBuilder.OpenConfigUi -= this.Open;
|
||||
}
|
||||
|
||||
private void Open(object? sender, EventArgs? e) {
|
||||
private void Open() {
|
||||
this.ShowSettings = true;
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ namespace NoSoliciting.Interface {
|
|||
var types = this.Plugin.Config.MlFilters[category];
|
||||
|
||||
void DrawTypes(ChatType type, string id) {
|
||||
var name = type.Name(this.Plugin.Interface.Data);
|
||||
var name = type.Name(this.Plugin.DataManager);
|
||||
|
||||
var check = types.Contains(type);
|
||||
if (!ImGui.Checkbox($"{name}##{id}", ref check)) {
|
||||
|
|
|
@ -220,7 +220,7 @@ namespace NoSoliciting {
|
|||
return "Party (Cross-world)";
|
||||
}
|
||||
|
||||
var lf = data.GetExcelSheet<LogFilter>().FirstOrDefault(lf => lf.LogKind == type.LogKind());
|
||||
var lf = data.GetExcelSheet<LogFilter>()!.FirstOrDefault(lf => lf.LogKind == type.LogKind());
|
||||
return lf?.Name?.ToString() ?? type.ToString();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
using System.IO;
|
||||
using Microsoft.ML;
|
||||
using NoSoliciting.Interface;
|
||||
|
||||
namespace NoSoliciting.Ml {
|
||||
internal class Classifier : IClassifier {
|
||||
private MLContext Context { get; set; } = null!;
|
||||
private ITransformer Model { get; set; } = null!;
|
||||
private DataViewSchema Schema { get; set; } = null!;
|
||||
private PredictionEngine<Data, Prediction>? PredictionEngine { get; set; }
|
||||
|
||||
public void Initialise(byte[] data) {
|
||||
if (this.PredictionEngine != null) {
|
||||
this.PredictionEngine.Dispose();
|
||||
this.PredictionEngine = null;
|
||||
}
|
||||
|
||||
this.Context = new MLContext();
|
||||
this.Context.ComponentCatalog.RegisterAssembly(typeof(Data).Assembly);
|
||||
using var stream = new MemoryStream(data);
|
||||
var model = this.Context.Model.Load(stream, out var schema);
|
||||
this.Model = model;
|
||||
this.Schema = schema;
|
||||
this.PredictionEngine = this.Context.Model.CreatePredictionEngine<Data, Prediction>(this.Model, this.Schema);
|
||||
}
|
||||
|
||||
public string Classify(ushort channel, string message) {
|
||||
return this.PredictionEngine?.Predict(new Data(channel, message))?.Category ?? "UNKNOWN";
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.PredictionEngine?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,13 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Plugin;
|
||||
using JKang.IpcServiceFramework.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Dalamud.Logging;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Resources;
|
||||
using Resourcer;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
@ -31,18 +27,16 @@ namespace NoSoliciting.Ml {
|
|||
public uint Version { get; }
|
||||
public Uri ReportUrl { get; }
|
||||
|
||||
private Process Process { get; }
|
||||
private IIpcClient<IClassifier> Classifier { get; }
|
||||
private IClassifier Classifier { get; }
|
||||
|
||||
private MlFilter(uint version, Uri reportUrl, Process process, IIpcClient<IClassifier> classifier) {
|
||||
this.Process = process;
|
||||
private MlFilter(uint version, Uri reportUrl, IClassifier classifier) {
|
||||
this.Classifier = classifier;
|
||||
this.Version = version;
|
||||
this.ReportUrl = reportUrl;
|
||||
}
|
||||
|
||||
public MessageCategory ClassifyMessage(ushort channel, string message) {
|
||||
var prediction = this.Classifier.InvokeAsync(classifier => classifier.Classify(channel, message)).Result;
|
||||
var prediction = this.Classifier.Classify(channel, message);
|
||||
var category = MessageCategoryExt.FromString(prediction);
|
||||
|
||||
if (category != null) {
|
||||
|
@ -71,7 +65,7 @@ namespace NoSoliciting.Ml {
|
|||
if (localManifest != null && (manifest?.Item1 == null || localManifest.Version == manifest.Value.manifest.Version)) {
|
||||
try {
|
||||
// try to reach the cached model
|
||||
data = File.ReadAllBytes(CachedFilePath(plugin, ModelName));
|
||||
data = await File.ReadAllBytesAsync(CachedFilePath(plugin, ModelName));
|
||||
// set the manifest to our local one and an empty string for the source
|
||||
manifest ??= (localManifest, string.Empty);
|
||||
} catch (IOException) {
|
||||
|
@ -128,64 +122,16 @@ namespace NoSoliciting.Ml {
|
|||
}
|
||||
|
||||
// initialise the classifier
|
||||
var pluginFolder = plugin.Interface.ConfigDirectory.ToString();
|
||||
|
||||
var exePath = await ExtractClassifier(pluginFolder);
|
||||
|
||||
var pipeId = Guid.NewGuid();
|
||||
|
||||
var process = StartClassifier(exePath, pipeId, showWindow);
|
||||
var client = await CreateClassifierClient(pipeId, data);
|
||||
var classifier = new Classifier();
|
||||
classifier.Initialise(data);
|
||||
|
||||
return new MlFilter(
|
||||
manifest.Value.manifest!.Version,
|
||||
manifest.Value.manifest!.ReportUrl,
|
||||
process!,
|
||||
client
|
||||
classifier
|
||||
);
|
||||
}
|
||||
|
||||
private static async Task<IIpcClient<IClassifier>> CreateClassifierClient(Guid pipeId, byte[] data) {
|
||||
var serviceProvider = new ServiceCollection()
|
||||
.AddNamedPipeIpcClient<IClassifier>("client", (_, options) => {
|
||||
options.PipeName = $"NoSoliciting.MessageClassifier-{pipeId}";
|
||||
options.Serializer = new BetterIpcSerialiser();
|
||||
})
|
||||
.BuildServiceProvider();
|
||||
|
||||
var clientFactory = serviceProvider.GetRequiredService<IIpcClientFactory<IClassifier>>();
|
||||
var client = clientFactory.CreateClient("client");
|
||||
|
||||
await client.InvokeAsync(classifier => classifier.Initialise(data));
|
||||
return client;
|
||||
}
|
||||
|
||||
private static Process StartClassifier(string exePath, Guid pipeId, bool showWindow) {
|
||||
var game = Process.GetCurrentProcess();
|
||||
|
||||
var startInfo = new ProcessStartInfo(exePath) {
|
||||
CreateNoWindow = !showWindow,
|
||||
UseShellExecute = false,
|
||||
Arguments = $"\"{game.Id}\" \"{game.ProcessName}\" \"{pipeId}\"",
|
||||
};
|
||||
return Process.Start(startInfo)!;
|
||||
}
|
||||
|
||||
private static async Task<string> ExtractClassifier(string pluginFolder) {
|
||||
using var exe = Resource.AsStream("NoSoliciting.NoSoliciting.MessageClassifier.exe");
|
||||
Directory.CreateDirectory(pluginFolder);
|
||||
var exePath = Path.Combine(pluginFolder, "NoSoliciting.MessageClassifier.exe");
|
||||
|
||||
try {
|
||||
using var exeFile = File.Create(exePath);
|
||||
await exe.CopyToAsync(exeFile);
|
||||
} catch (IOException ex) {
|
||||
PluginLog.LogWarning($"Could not update classifier. Continuing as normal.\n{ex}");
|
||||
}
|
||||
|
||||
return exePath;
|
||||
}
|
||||
|
||||
private static async Task<byte[]?> DownloadModel(Uri url) {
|
||||
try {
|
||||
using var client = new WebClient();
|
||||
|
@ -211,7 +157,7 @@ namespace NoSoliciting.Ml {
|
|||
var file = File.Create(cachePath);
|
||||
await file.WriteAsync(data, 0, data.Length);
|
||||
await file.FlushAsync();
|
||||
file.Dispose();
|
||||
await file.DisposeAsync();
|
||||
}
|
||||
|
||||
private static async Task<(Manifest manifest, string source)?> DownloadManifest() {
|
||||
|
@ -257,12 +203,7 @@ namespace NoSoliciting.Ml {
|
|||
}
|
||||
|
||||
public void Dispose() {
|
||||
try {
|
||||
this.Process.Kill();
|
||||
this.Process.Dispose();
|
||||
} catch (Exception) {
|
||||
// ignored
|
||||
}
|
||||
this.Classifier.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<Version>2.1.3</Version>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<TargetFramework>net5-windows</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
|
@ -35,18 +37,14 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="1.2.1"/>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.2"/>
|
||||
<PackageReference Include="Fody" Version="6.5.2" PrivateAssets="all"/>
|
||||
<PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.18.2"/>
|
||||
<PackageReference Include="JKang.IpcServiceFramework.Client.NamedPipe" Version="3.1.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2"/>
|
||||
<PackageReference Include="Microsoft.ML" Version="1.6.0"/>
|
||||
<PackageReference Include="Resourcer.Fody" Version="1.8.0" PrivateAssets="all"/>
|
||||
<PackageReference Include="ResourcesMerge.Fody" Version="1.0.3" PrivateAssets="all"/>
|
||||
<PackageReference Include="XivCommon" Version="2.2.0"/>
|
||||
<PackageReference Include="XivCommon" Version="3.0.1"/>
|
||||
<PackageReference Include="YamlDotNet" Version="11.2.1"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\NoSoliciting.MessageClassifier\bin\Release\net48\NoSoliciting.MessageClassifier.exe"/>
|
||||
<EmbeddedResource Update="Resources\Language.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Language.Designer.cs</LastGenOutput>
|
||||
|
@ -62,4 +60,7 @@
|
|||
<DependentUpon>NoSoliciting.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="..\icon.png" Link="images/icon.png" CopyToOutputDirectory="PreserveNewest" Visible="false"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
author: ascclemens
|
||||
name: NoSoliciting
|
||||
punchline: Adblock for FFXIV.
|
||||
description: |-
|
||||
Customisable chat and Party Finder filtering. In addition to letting
|
||||
you filter anything from chat and PF, it comes with built-in filters
|
||||
|
|
|
@ -6,52 +6,79 @@ using System.IO;
|
|||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud;
|
||||
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.Logging;
|
||||
using NoSoliciting.Interface;
|
||||
using NoSoliciting.Ml;
|
||||
using NoSoliciting.Resources;
|
||||
using XivCommon;
|
||||
|
||||
namespace NoSoliciting {
|
||||
// ReSharper disable once ClassNeverInstantiated.Global
|
||||
public class Plugin : IDalamudPlugin {
|
||||
private bool _disposedValue;
|
||||
|
||||
public string Name => "NoSoliciting";
|
||||
|
||||
private Filter Filter { get; set; } = null!;
|
||||
private Filter Filter { get; }
|
||||
|
||||
public DalamudPluginInterface Interface { get; private set; } = null!;
|
||||
public PluginConfiguration Config { get; private set; } = null!;
|
||||
public XivCommonBase Common { get; private set; } = null!;
|
||||
public PluginUi Ui { get; private set; } = null!;
|
||||
public Commands Commands { get; private set; } = null!;
|
||||
private ContextMenu ContextMenu { get; set; } = null!;
|
||||
public MlFilterStatus MlStatus { get; set; } = MlFilterStatus.Uninitialised;
|
||||
public MlFilter? MlFilter { get; set; }
|
||||
[PluginService]
|
||||
internal DalamudPluginInterface Interface { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
private ClientState ClientState { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal ChatGui ChatGui { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal PartyFinderGui PartyFinderGui { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal DataManager DataManager { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal CommandManager CommandManager { get; init; } = null!;
|
||||
|
||||
[PluginService]
|
||||
internal ToastGui ToastGui { get; init; } = null!;
|
||||
|
||||
internal PluginConfiguration Config { get; }
|
||||
internal XivCommonBase Common { get; }
|
||||
internal PluginUi Ui { get; }
|
||||
private Commands Commands { get; }
|
||||
private ContextMenu ContextMenu { get; }
|
||||
internal MlFilterStatus MlStatus { get; set; } = MlFilterStatus.Uninitialised;
|
||||
internal MlFilter? MlFilter { get; set; }
|
||||
|
||||
private readonly List<Message> _messageHistory = new();
|
||||
public IEnumerable<Message> MessageHistory => this._messageHistory;
|
||||
internal IEnumerable<Message> MessageHistory => this._messageHistory;
|
||||
|
||||
private readonly List<Message> _partyFinderHistory = new();
|
||||
public IEnumerable<Message> PartyFinderHistory => this._partyFinderHistory;
|
||||
internal IEnumerable<Message> PartyFinderHistory => this._partyFinderHistory;
|
||||
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local
|
||||
public string AssemblyLocation { get; private set; } = Assembly.GetExecutingAssembly().Location;
|
||||
|
||||
public void Initialize(DalamudPluginInterface pluginInterface) {
|
||||
public Plugin() {
|
||||
string path = Environment.GetEnvironmentVariable("PATH")!;
|
||||
string newPath = Path.GetDirectoryName(this.AssemblyLocation)!;
|
||||
Environment.SetEnvironmentVariable("PATH", $"{path};{newPath}");
|
||||
|
||||
this.Interface = pluginInterface;
|
||||
|
||||
this.Config = this.Interface.GetPluginConfig() as PluginConfiguration ?? new PluginConfiguration();
|
||||
this.Config.Initialise(this.Interface);
|
||||
|
||||
this.ConfigureLanguage();
|
||||
this.Interface.OnLanguageChanged += this.OnLanguageUpdate;
|
||||
this.Interface.LanguageChanged += this.OnLanguageUpdate;
|
||||
|
||||
this.Common = new XivCommonBase(this.Interface, Hooks.PartyFinder | Hooks.ContextMenu);
|
||||
this.Common = new XivCommonBase(Hooks.PartyFinder | Hooks.ContextMenu);
|
||||
|
||||
this.Ui = new PluginUi(this);
|
||||
this.Commands = new Commands(this);
|
||||
|
@ -63,7 +90,7 @@ namespace NoSoliciting {
|
|||
|
||||
// pre-compute the max ilvl to prevent stutter
|
||||
try {
|
||||
FilterUtil.MaxItemLevelAttainable(this.Interface.Data);
|
||||
FilterUtil.MaxItemLevelAttainable(this.DataManager);
|
||||
} catch (Exception ex) {
|
||||
PluginLog.LogError(ex, "Exception while computing max item level");
|
||||
}
|
||||
|
@ -81,7 +108,7 @@ namespace NoSoliciting {
|
|||
this.Commands.Dispose();
|
||||
this.Ui.Dispose();
|
||||
this.Common.Dispose();
|
||||
this.Interface.OnLanguageChanged -= this.OnLanguageUpdate;
|
||||
this.Interface.LanguageChanged -= this.OnLanguageUpdate;
|
||||
}
|
||||
|
||||
this._disposedValue = true;
|
||||
|
@ -93,17 +120,19 @@ namespace NoSoliciting {
|
|||
|
||||
internal void ConfigureLanguage(string? langCode = null) {
|
||||
if (this.Config.FollowGameLanguage) {
|
||||
langCode = this.Interface.ClientState.ClientLanguage switch {
|
||||
langCode = this.ClientState.ClientLanguage switch {
|
||||
ClientLanguage.Japanese => "ja",
|
||||
ClientLanguage.English => "en",
|
||||
ClientLanguage.German => "de",
|
||||
ClientLanguage.French => "fr",
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(this.ClientState.ClientLanguage), "Unknown ClientLanguage"),
|
||||
};
|
||||
}
|
||||
|
||||
langCode ??= this.Interface.UiLanguage;
|
||||
Resources.Language.Culture = new CultureInfo(langCode ?? "en");
|
||||
// I don't fucking trust this. Not since last time.
|
||||
// ReSharper disable once ConstantNullCoalescingCondition
|
||||
Language.Culture = new CultureInfo(langCode ?? "en");
|
||||
}
|
||||
|
||||
internal void InitialiseMachineLearning(bool showWindow) {
|
||||
|
|
|
@ -109,7 +109,7 @@ namespace NoSoliciting {
|
|||
}
|
||||
|
||||
internal bool MlEnabledOn(MessageCategory category, ChatType chatType) {
|
||||
HashSet<ChatType> filtered;
|
||||
HashSet<ChatType>? filtered;
|
||||
|
||||
if (this.AdvancedMode) {
|
||||
if (!this.MlFilters.TryGetValue(category, out filtered)) {
|
||||
|
|
|
@ -169,7 +169,7 @@ namespace NoSoliciting.Resources {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Messages that don't full into other categories and are not for content (Party Finder).
|
||||
/// Looks up a localized string similar to Messages that don't fall into other categories and are not for content (Party Finder).
|
||||
/// </summary>
|
||||
internal static string FluffDescription {
|
||||
get {
|
||||
|
|
|
@ -280,6 +280,6 @@
|
|||
<value>Fluff</value>
|
||||
</data>
|
||||
<data name="FluffDescription" xml:space="preserve">
|
||||
<value>Messages that don't full into other categories and are not for content (Party Finder)</value>
|
||||
<value>Messages that don't fall into other categories and are not for content (Party Finder)</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="458.08871"
|
||||
height="458.08871"
|
||||
viewBox="0 0 121.20264 121.20264"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:export-filename="C:\Users\Anna\Desktop\nosol.png"
|
||||
inkscape:export-xdpi="107.298"
|
||||
inkscape:export-ydpi="107.298"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
sodipodi:docname="nosol.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.7039681"
|
||||
inkscape:cx="175.7662"
|
||||
inkscape:cy="230.34468"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1592"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
units="px"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-53.447652,-85.066737)">
|
||||
<path
|
||||
sodipodi:type="star"
|
||||
style="fill:#af3310;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:8;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path33"
|
||||
inkscape:flatsided="true"
|
||||
sodipodi:sides="8"
|
||||
sodipodi:cx="-396.65961"
|
||||
sodipodi:cy="345.39066"
|
||||
sodipodi:r1="243.58626"
|
||||
sodipodi:r2="225.04436"
|
||||
sodipodi:arg1="0"
|
||||
sodipodi:arg2="0.39269908"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m -153.07335,345.39066 -71.34476,172.24149 -172.2415,71.34476 -172.24149,-71.34476 -71.34476,-172.24149 71.34476,-172.2415 172.24149,-71.34476 172.2415,71.34476 z"
|
||||
transform="matrix(0.24444312,0.10125166,-0.10125166,0.24444312,245.98106,101.40213)"
|
||||
inkscape:transform-center-x="-1.8847157e-06"
|
||||
inkscape:transform-center-y="-3.1424378e-06"
|
||||
inkscape:export-filename="C:\Users\Anna\Desktop\path33.png"
|
||||
inkscape:export-xdpi="61.923672"
|
||||
inkscape:export-ydpi="61.923672" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:38.4628px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="57.772713"
|
||||
y="159.98831"
|
||||
id="text3416"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3414"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.265;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="57.772713"
|
||||
y="159.98831">NoSol</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
Loading…
Reference in New Issue