feat: make reporting better

This commit is contained in:
Anna 2021-05-15 18:19:27 -04:00
parent 02f020c399
commit 5a1990d30c
Signed by: anna
GPG Key ID: 0B391D8F06FCD9E0
17 changed files with 300 additions and 131 deletions

View File

@ -39,6 +39,6 @@
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="1.2.1" />
<PackageReference Include="Fody" Version="6.5.1" PrivateAssets="all" />
<PackageReference Include="ResourcesMerge.Fody" Version="1.0.1" PrivateAssets="all" />
<PackageReference Include="ResourcesMerge.Fody" Version="1.0.3" PrivateAssets="all" />
</ItemGroup>
</Project>

View File

@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="ConsoleTables" Version="2.4.2"/>
<PackageReference Include="CsvHelper" Version="27.0.1"/>
<PackageReference Include="CsvHelper" Version="27.0.2"/>
<PackageReference Include="Microsoft.ML" Version="1.5.5"/>
</ItemGroup>

View File

@ -50,21 +50,7 @@ namespace NoSoliciting {
return;
}
Task.Run(async () => {
var status = await this.Plugin.Ui.Report.ReportMessageAsync(message);
switch (status) {
case ReportStatus.Successful: {
var msg = Language.ReportToastSuccess;
this.Plugin.Interface.Framework.Gui.Toast.ShowNormal(string.Format(msg, listing.Name));
break;
}
case ReportStatus.Failure: {
var msg = Language.ReportToastFailure;
this.Plugin.Interface.Framework.Gui.Toast.ShowError(string.Format(msg, listing.Name));
break;
}
}
});
this.Plugin.Ui.Report.ToShowModal = message;
}
}
}

View File

@ -84,23 +84,20 @@ namespace NoSoliciting {
this.LastBatch = args.BatchNumber;
var version = this.Plugin.MlFilter?.Version;
var reason = this.MlListingFilterReason(listing);
if (version == null) {
return;
}
var (category, reason) = this.MlListingFilterReason(listing);
this.Plugin.AddPartyFinderHistory(new Message(
version.Value,
version,
ChatType.None,
listing.ContentIdLower,
listing.Name,
listing.Description,
true,
reason
category,
reason == "custom",
reason == "ilvl"
));
if (reason == null) {
if (category == null && reason == null) {
return;
}
@ -123,10 +120,6 @@ namespace NoSoliciting {
}
private bool MlFilterMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
if (this.Plugin.MlFilter == null) {
return false;
}
var chatType = ChatTypeExt.FromDalamud(type);
// NOTE: don't filter on user-controlled chat types here because custom filters are supposed to check all
@ -137,80 +130,81 @@ namespace NoSoliciting {
var text = message.TextValue;
string? reason = null;
var custom = false;
MessageCategory? classification = null;
// step 1. check for custom filters if enabled
var filter = this.Plugin.Config.CustomChatFilter
&& Chat.MatchesCustomFilters(text, this.Plugin.Config)
&& SetReason(out reason, "custom");
var filter = false;
if (this.Plugin.Config.CustomChatFilter && Chat.MatchesCustomFilters(text, this.Plugin.Config)) {
filter = true;
custom = true;
}
// only look at ml if message >= min words
if (!filter && text.Trim().Split(' ').Length >= MinWords) {
if (!filter && this.Plugin.MlFilter != null && text.Trim().Split(' ').Length >= MinWords) {
// step 2. classify the message using the model
var category = this.Plugin.MlFilter.ClassifyMessage((ushort) chatType, text);
// step 2a. only filter if configured to act on this channel
filter = category != MessageCategory.Normal
&& this.Plugin.Config.MlEnabledOn(category, chatType)
&& SetReason(out reason, category.Name());
if (category != MessageCategory.Normal && this.Plugin.Config.MlEnabledOn(category, chatType)) {
filter = true;
classification = category;
}
}
this.Plugin.AddMessageHistory(new Message(
this.Plugin.MlFilter.Version,
var history = new Message(
this.Plugin.MlFilter?.Version,
ChatTypeExt.FromDalamud(type),
senderId,
sender,
message,
true,
reason
));
classification,
custom,
false
);
this.Plugin.AddMessageHistory(history);
if (filter && this.Plugin.Config.LogFilteredChat) {
PluginLog.Log($"Filtered chat message ({reason}): {text}");
PluginLog.Log($"Filtered chat message ({history.FilterReason ?? "unknown"}): {text}");
}
return filter;
}
private string? MlListingFilterReason(PartyFinderListing listing) {
private (MessageCategory?, string?) MlListingFilterReason(PartyFinderListing listing) {
if (this.Plugin.MlFilter == null) {
return null;
return (null, null);
}
// ignore private listings if configured
if (!this.Plugin.Config.ConsiderPrivatePfs && listing[SearchAreaFlags.Private]) {
return null;
return (null, null);
}
var desc = listing.Description.TextValue;
// 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)) {
return "ilvl";
return (null, "ilvl");
}
// step 2. check custom filters
if (this.Plugin.Config.CustomPFFilter && PartyFinder.MatchesCustomFilters(desc, this.Plugin.Config)) {
return "custom";
return (null, "custom");
}
// only look at ml for pfs >= min words
if (desc.Trim().Spacify().Split(' ').Length < MinWords) {
return null;
return (null, null);
}
var category = this.Plugin.MlFilter.ClassifyMessage((ushort) ChatType.None, desc);
if (category != MessageCategory.Normal && this.Plugin.Config.MlEnabledOn(category, ChatType.None)) {
return category.Name();
return (category, null);
}
return null;
}
private static bool SetReason(out string reason, string value) {
reason = value;
return true;
return (null, null);
}
}
}

View File

@ -3,12 +3,12 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Plugin;
using ImGuiNET;
using NoSoliciting.Ml;
using NoSoliciting.Resources;
namespace NoSoliciting.Interface {
@ -34,6 +34,8 @@ namespace NoSoliciting.Interface {
set => this._showReporting = value;
}
internal Message? ToShowModal { get; set; }
public Report(Plugin plugin) {
this.Plugin = plugin;
}
@ -47,6 +49,13 @@ namespace NoSoliciting.Interface {
}
public void Draw() {
var toShow = this.ToShowModal;
if (toShow != null) {
if (!this.SetUpReportModal(toShow)) {
ImGui.OpenPopup($"###modal-message-{toShow.Id}");
}
}
if (!this.ShowReporting) {
return;
}
@ -102,7 +111,7 @@ namespace NoSoliciting.Interface {
foreach (var message in this.Plugin.MessageHistory) {
ImGui.TableNextRow();
if (message.FilterReason != null) {
if (message.Filtered) {
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
}
@ -112,11 +121,11 @@ namespace NoSoliciting.Interface {
.Select(payload => payload.Text)
.FirstOrDefault() ?? "";
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.ChatType.Name(this.Plugin.Interface.Data), message.FilterReason ?? "", sender, message.Content.TextValue)) {
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.ChatType.Name(this.Plugin.Interface.Data), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
ImGui.OpenPopup($"###modal-message-{message.Id}");
}
if (message.FilterReason != null) {
if (message.Filtered) {
ImGui.PopStyleColor();
}
@ -142,7 +151,7 @@ namespace NoSoliciting.Interface {
var builder = new StringBuilder();
foreach (var message in this.Plugin.PartyFinderHistory) {
if (message.FilterReason == null) {
if (message.Classification == null) {
continue;
}
@ -165,7 +174,7 @@ namespace NoSoliciting.Interface {
foreach (var message in this.Plugin.PartyFinderHistory) {
ImGui.TableNextRow();
if (message.FilterReason != null) {
if (message.Filtered) {
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
}
@ -175,11 +184,11 @@ namespace NoSoliciting.Interface {
.Select(payload => payload.Text)
.FirstOrDefault() ?? "";
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.FilterReason ?? "", sender, message.Content.TextValue)) {
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
ImGui.OpenPopup($"###modal-message-{message.Id}");
}
if (message.FilterReason != null) {
if (message.Filtered) {
ImGui.PopStyleColor();
}
@ -197,50 +206,129 @@ namespace NoSoliciting.Interface {
#region Modal
private void SetUpReportModal(Message message) {
private MessageCategory? _reportCategory;
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <returns>true if modal is closing</returns>
private bool SetUpReportModal(Message message) {
var closing = false;
ImGui.SetNextWindowSize(new Vector2(350, -1));
var modalTitle = string.Format(Language.ReportModalTitle, this.Plugin.Name);
if (!ImGui.BeginPopupModal($"{modalTitle}###modal-message-{message.Id}")) {
return;
return false;
}
if (this._reportCategory == null) {
if (message.Classification != null) {
this._reportCategory = message.Classification;
} else if (message.Classification == null && !message.Custom && !message.ItemLevel) {
this._reportCategory = MessageCategory.Normal;
}
}
ImGui.PushTextWrapPos();
ImGui.TextUnformatted(Language.ReportModalHelp1);
ImGui.TextUnformatted(message.FilterReason != null
? Language.ReportModalWasFiltered
: Language.ReportModalWasNotFiltered);
ImGui.Separator();
ImGui.TextUnformatted(message.Content.TextValue);
ImGui.Separator();
ImGui.TextUnformatted(string.Format(Language.ReportModalOriginalClassification, message.FilterReason ?? MessageCategory.Normal.Name()));
ImGui.TextUnformatted(Language.ReportModalSuggestedClassification);
ImGui.SetNextItemWidth(-1);
if (ImGui.BeginCombo($"##modal-classification-{message.Id}", this._reportCategory?.Name() ?? string.Empty)) {
foreach (var category in (MessageCategory[]) Enum.GetValues(typeof(MessageCategory))) {
if (ImGui.Selectable($"{category.Name()}##modal-option-{message.Id}", this._reportCategory == category)) {
this._reportCategory = category;
}
if (!ImGui.IsItemHovered()) {
continue;
}
ImGui.BeginTooltip();
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 24);
ImGui.TextUnformatted(category.Description());
ImGui.PopTextWrapPos();
ImGui.EndTooltip();
}
ImGui.EndCombo();
}
ImGui.Separator();
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
ImGui.TextUnformatted(Language.ReportModalHelp2);
ImGui.PopStyleColor();
ImGui.Separator();
if (message.FilterReason == "custom") {
string? errorText = null;
if (message.Custom) {
errorText = Language.ReportModalDisabledCustom;
} else if (message.ItemLevel) {
errorText = Language.ReportModalDisabledItemLevel;
} else if (message.ModelVersion == null) {
errorText = Language.ReportModalDisabledBadModel;
} else if (this._reportCategory == message.Classification) {
errorText = Language.ReportModalDisabledSameClassification;
} else {
switch (this.Plugin.Config.AdvancedMode) {
case true when this.Plugin.Config.MlFilters.Values.All(set => set.Count == 0):
case false when this.Plugin.Config.BasicMlFilters.Count == 0:
errorText = Language.ReportModalDisabledNoFilters;
break;
}
}
if (errorText != null) {
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
ImGui.TextUnformatted(Language.ReportModalCustom);
ImGui.TextUnformatted(errorText);
ImGui.PopStyleColor();
} else {
var buttonTitle =Language.ReportModalReport;
if (ImGui.Button($"{buttonTitle}##report-submit-{message.Id}")) {
this.ReportMessage(message);
if (ImGui.Button($"{Language.ReportModalReport}##report-submit-{message.Id}")) {
var suggested = this._reportCategory?.ToModelName() ?? "none (this is a bug)";
this._reportCategory = null;
if (message == this.ToShowModal) {
Task.Run(async () => {
var status = await this.Plugin.Ui.Report.ReportMessageAsync(message, suggested);
switch (status) {
case ReportStatus.Successful: {
var msg = Language.ReportToastSuccess;
this.Plugin.Interface.Framework.Gui.Toast.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));
break;
}
}
});
this.ToShowModal = null;
} else {
this.ReportMessage(message, suggested);
}
ImGui.CloseCurrentPopup();
closing = true;
}
ImGui.SameLine();
}
var copyButton = Language.ReportModalCopy;
if (ImGui.Button($"{copyButton}##report-copy-{message.Id}")) {
if (ImGui.Button($"{Language.ReportModalCopy}##report-copy-{message.Id}")) {
ImGui.SetClipboardText(message.Content.TextValue);
}
@ -255,12 +343,20 @@ namespace NoSoliciting.Interface {
var cancelButton = Language.ReportModalCancel;
if (ImGui.Button($"{cancelButton}##report-cancel-{message.Id}")) {
this._reportCategory = null;
if (message == this.ToShowModal) {
this.ToShowModal = null;
}
ImGui.CloseCurrentPopup();
closing = true;
}
ImGui.PopTextWrapPos();
ImGui.EndPopup();
return closing;
}
#endregion
@ -292,18 +388,21 @@ namespace NoSoliciting.Interface {
return clicked;
}
internal void ReportMessage(Message message) {
Task.Run(async () => await this.ReportMessageAsync(message));
private void ReportMessage(Message message, string suggested) {
Task.Run(async () => await this.ReportMessageAsync(message, suggested));
}
internal async Task<ReportStatus> ReportMessageAsync(Message message) {
private async Task<ReportStatus> ReportMessageAsync(Message message, string suggested) {
string? resp = null;
try {
using var client = new WebClient();
this.LastReportStatus = ReportStatus.InProgress;
var reportUrl = this.Plugin.MlFilter?.ReportUrl;
if (reportUrl != null) {
resp = await client.UploadStringTaskAsync(reportUrl, message.ToJson()).ConfigureAwait(true);
var json = message.ToJson(suggested);
if (json != null) {
resp = await client.UploadStringTaskAsync(reportUrl, json).ConfigureAwait(true);
}
}
} catch (Exception) {
// ignored

View File

@ -6,66 +6,87 @@ using System.Linq;
using Dalamud.Data;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Lumina.Excel.GeneratedSheets;
using NoSoliciting.Ml;
#if DEBUG
using System.Text;
using NoSoliciting.Ml;
#endif
namespace NoSoliciting {
[Serializable]
public class Message {
public Guid Id { get; }
[JsonIgnore]
public uint ActorId { get; }
public uint DefinitionsVersion { get; }
public uint? ModelVersion { get; }
public DateTime Timestamp { get; }
public ChatType ChatType { get; }
public SeString Sender { get; }
public SeString Content { get; }
public bool Ml { get; }
public string? FilterReason { get; }
public Message(uint defsVersion, ChatType type, uint actorId, SeString sender, SeString content, bool ml, string? reason) {
public MessageCategory? Classification { get; }
public bool Custom { get; }
public bool ItemLevel { get; }
public bool Filtered => this.Custom || this.ItemLevel || this.Classification != null;
public string? FilterReason => this.Custom
? "custom"
: this.ItemLevel
? "ilvl"
: this.Classification?.Name();
internal Message(uint? defsVersion, ChatType type, uint actorId, SeString sender, SeString content, MessageCategory? classification, bool custom, bool ilvl) {
this.Id = Guid.NewGuid();
this.DefinitionsVersion = defsVersion;
this.ModelVersion = defsVersion;
this.Timestamp = DateTime.Now;
this.ChatType = type;
this.ActorId = actorId;
this.Sender = sender;
this.Content = content;
this.Ml = ml;
this.FilterReason = reason;
this.Classification = classification;
this.Custom = custom;
this.ItemLevel = ilvl;
}
[Serializable]
[JsonObject(NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
private class JsonMessage {
public Guid Id { get; set; }
public uint DefinitionsVersion { get; set; }
public uint ReportVersion { get; } = 2;
public uint ModelVersion { get; set; }
public DateTime Timestamp { get; set; }
public ushort Type { get; set; }
// note: cannot use byte[] because Newtonsoft thinks it's a good idea to always base64 byte[]
// and I don't want to write a custom converter to overwrite their stupiditiy
// and I don't want to write a custom converter to overwrite their stupidity
public List<byte> Sender { get; set; }
public List<byte> Content { get; set; }
public bool Ml { get; set; }
public string? Reason { get; set; }
public string? SuggestedClassification { get; set; }
}
public string ToJson() {
public string? ToJson(string suggested) {
if (this.ModelVersion == null) {
return null;
}
var msg = new JsonMessage {
Id = this.Id,
DefinitionsVersion = this.DefinitionsVersion,
ModelVersion = this.ModelVersion.Value,
Timestamp = this.Timestamp,
Type = (ushort) this.ChatType,
Sender = this.Sender.Encode().ToList(),
Content = this.Content.Encode().ToList(),
Ml = this.Ml,
Reason = this.FilterReason,
Reason = this.Custom
? "custom"
: this.ItemLevel
? "ilvl"
: this.Classification?.ToModelName() ?? "unknown",
SuggestedClassification = suggested,
};
return JsonConvert.SerializeObject(msg, new JsonSerializerSettings {
@ -77,9 +98,7 @@ namespace NoSoliciting {
public StringBuilder ToCsv(StringBuilder? builder = null) {
builder ??= new StringBuilder();
var category = MessageCategoryExt.FromName(this.FilterReason) ?? MessageCategory.Normal;
builder.Append(category.ToModelName());
builder.Append(this.Classification?.ToModelName());
builder.Append(',');
builder.Append((int) this.ChatType);
builder.Append(",\"");

View File

@ -43,20 +43,6 @@ namespace NoSoliciting.Ml {
};
#if DEBUG
public static string ToModelName(this MessageCategory category) => category switch {
MessageCategory.Trade => "TRADE",
MessageCategory.FreeCompany => "FC",
MessageCategory.Normal => "NORMAL",
MessageCategory.Phishing => "PHISH",
MessageCategory.RmtContent => "RMT_C",
MessageCategory.RmtGil => "RMT_G",
MessageCategory.Roleplaying => "RP",
MessageCategory.Static => "STATIC",
MessageCategory.Community => "COMMUNITY",
MessageCategory.StaticSub => "STATIC_SUB",
_ => throw new ArgumentException("Invalid category", nameof(category)),
};
public static MessageCategory? FromName(string? category) => category switch {
"Trade ads" => MessageCategory.Trade,
"Free Company ads" => MessageCategory.FreeCompany,
@ -70,9 +56,22 @@ namespace NoSoliciting.Ml {
"Static substitutes" => MessageCategory.StaticSub,
_ => null,
};
#endif
public static string ToModelName(this MessageCategory category) => category switch {
MessageCategory.Trade => "TRADE",
MessageCategory.FreeCompany => "FC",
MessageCategory.Normal => "NORMAL",
MessageCategory.Phishing => "PHISH",
MessageCategory.RmtContent => "RMT_C",
MessageCategory.RmtGil => "RMT_G",
MessageCategory.Roleplaying => "RP",
MessageCategory.Static => "STATIC",
MessageCategory.Community => "COMMUNITY",
MessageCategory.StaticSub => "STATIC_SUB",
_ => throw new ArgumentException("Invalid category", nameof(category)),
};
public static string Name(this MessageCategory category) => category switch {
MessageCategory.Trade => Language.TradeCategory,
MessageCategory.FreeCompany => Language.FreeCompanyCategory,

View File

@ -41,7 +41,7 @@
<PackageReference Include="JKang.IpcServiceFramework.Client.NamedPipe" Version="3.1.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1"/>
<PackageReference Include="Resourcer.Fody" Version="1.8.0" PrivateAssets="all"/>
<PackageReference Include="ResourcesMerge.Fody" Version="1.0.1" PrivateAssets="all"/>
<PackageReference Include="ResourcesMerge.Fody" Version="1.0.3" PrivateAssets="all"/>
<PackageReference Include="XivCommon" Version="1.5.0"/>
<PackageReference Include="YamlDotNet" Version="11.1.1"/>
</ItemGroup>

View File

@ -474,12 +474,48 @@ namespace NoSoliciting.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Reporting is disabled because your ML model was not functioning when you saw this message..
/// </summary>
internal static string ReportModalDisabledBadModel {
get {
return ResourceManager.GetString("ReportModalDisabledBadModel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot report messages filtered because of a custom filter..
/// </summary>
internal static string ReportModalCustom {
internal static string ReportModalDisabledCustom {
get {
return ResourceManager.GetString("ReportModalCustom", resourceCulture);
return ResourceManager.GetString("ReportModalDisabledCustom", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You cannot report messages filtered because of item level..
/// </summary>
internal static string ReportModalDisabledItemLevel {
get {
return ResourceManager.GetString("ReportModalDisabledItemLevel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reporting is disabled because you do not have any filters enabled..
/// </summary>
internal static string ReportModalDisabledNoFilters {
get {
return ResourceManager.GetString("ReportModalDisabledNoFilters", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reporting is disabled because you must choose a different classification than the original..
/// </summary>
internal static string ReportModalDisabledSameClassification {
get {
return ResourceManager.GetString("ReportModalDisabledSameClassification", resourceCulture);
}
}
@ -501,6 +537,15 @@ namespace NoSoliciting.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to This message&apos;s original classification: {0}.
/// </summary>
internal static string ReportModalOriginalClassification {
get {
return ResourceManager.GetString("ReportModalOriginalClassification", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Report.
/// </summary>
@ -510,6 +555,15 @@ namespace NoSoliciting.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to How do you think this message should have been classified?.
/// </summary>
internal static string ReportModalSuggestedClassification {
get {
return ResourceManager.GetString("ReportModalSuggestedClassification", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Report to {0}.
/// </summary>

View File

@ -132,7 +132,7 @@
<data name="ReportModalReport" xml:space="preserve">
<value>Melden</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>Du kannst Nachrichten, die aufgrund von benutzerdefinierten Filtern gefiltert wurden, nicht melden.</value>
</data>
<data name="PhishingCategory" xml:space="preserve">

View File

@ -172,7 +172,7 @@
<data name="ReportModalHelp2" xml:space="preserve">
<value>NoSoliciting sólo funciona con mensajes en inglés. No informes de mensajes en otros idiomas.</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>No puedes informar de mensajes que han sido filtrados debido a un filtro personalizado.</value>
</data>
<data name="ReportModalReport" xml:space="preserve">

View File

@ -219,7 +219,7 @@
<data name="ReportModalHelp2" xml:space="preserve">
<value>NoSoliciting n'est fonctionnel que pour les messages en anglais. Veuillez ne pas signaler des messages qui ne sont pas en anglais.</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>Vous ne pouvez pas signaler de messages filtrés à cause d'un filtre personnalisé.</value>
</data>
<data name="ReportModalReport" xml:space="preserve">

View File

@ -172,7 +172,7 @@
<data name="ReportModalHelp2" xml:space="preserve">
<value>NoSolicitingは英語のメッセージにのみ有効です。 他の言語でメッセージを報告しないでください。</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>カスタムフィルタでフィルタリングされたメッセージを報告することはできません。</value>
</data>
<data name="ReportModalReport" xml:space="preserve">

View File

@ -181,7 +181,7 @@
<data name="ReportModalWasNotFiltered" xml:space="preserve">
<value>De maneira específica, essa mensagem NÃO FOI filtrada, mas deveria ter sido.</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>Voçê não pode relatar as mensagens filtradas por causa de um filtro personalizado.</value>
</data>
<data name="ReportModalReport" xml:space="preserve">

View File

@ -3,7 +3,7 @@
<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">
@ -153,7 +153,7 @@
<data name="ReportModalHelp2" xml:space="preserve">
<value>NoSoliciting only works for English messages. Do not report non-English messages.</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>You cannot report messages filtered because of a custom filter.</value>
</data>
<data name="ReportModalReport" xml:space="preserve">
@ -255,4 +255,22 @@
<data name="ModelStatusInitialised" xml:space="preserve">
<value>Initialised</value>
</data>
</root>
<data name="ReportModalDisabledBadModel" xml:space="preserve">
<value>Reporting is disabled because your ML model was not functioning when you saw this message.</value>
</data>
<data name="ReportModalDisabledNoFilters" xml:space="preserve">
<value>Reporting is disabled because you do not have any filters enabled.</value>
</data>
<data name="ReportModalDisabledSameClassification" xml:space="preserve">
<value>Reporting is disabled because you must choose a different classification than the original.</value>
</data>
<data name="ReportModalDisabledItemLevel" xml:space="preserve">
<value>You cannot report messages filtered because of item level.</value>
</data>
<data name="ReportModalOriginalClassification" xml:space="preserve">
<value>This message's original classification: {0}</value>
</data>
<data name="ReportModalSuggestedClassification" xml:space="preserve">
<value>How do you think this message should have been classified?</value>
</data>
</root>

View File

@ -175,7 +175,7 @@
<data name="ReportModalWasNotFiltered" xml:space="preserve">
<value>具体来说,这是一条应该被过滤的消息,但它被放过了。</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>你不能报告被自定义过滤规则过滤的消息。</value>
</data>
<data name="ReportModalReport" xml:space="preserve">

View File

@ -64,7 +64,7 @@
<data name="ReportModalHelp1" xml:space="preserve">
<value>報告此消息將使開發人員知道此消息被錯誤歸類。</value>
</data>
<data name="ReportModalCustom" xml:space="preserve">
<data name="ReportModalDisabledCustom" xml:space="preserve">
<value>你不能報告被客製化過濾規則過濾的消息。</value>
</data>
<data name="OtherTab" xml:space="preserve">