458 lines
17 KiB
C#
Executable File
458 lines
17 KiB
C#
Executable File
using System;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Numerics;
|
|
using System.Threading.Tasks;
|
|
using Dalamud.Game.Text.SeStringHandling;
|
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|
using Dalamud.Logging;
|
|
using ImGuiNET;
|
|
using NoSoliciting.Ml;
|
|
using NoSoliciting.Resources;
|
|
|
|
#if DEBUG
|
|
using System.Text;
|
|
#endif
|
|
|
|
namespace NoSoliciting.Interface {
|
|
public class Report {
|
|
private const ImGuiTableFlags TableFlags = ImGuiTableFlags.Borders
|
|
& ~ImGuiTableFlags.BordersOuterV
|
|
| ImGuiTableFlags.PadOuterX
|
|
| ImGuiTableFlags.RowBg
|
|
| ImGuiTableFlags.SizingFixedFit
|
|
| ImGuiTableFlags.ScrollY
|
|
| ImGuiTableFlags.Hideable
|
|
| ImGuiTableFlags.Reorderable
|
|
| ImGuiTableFlags.Resizable;
|
|
|
|
private Plugin Plugin { get; }
|
|
|
|
private ReportStatus LastReportStatus { get; set; } = ReportStatus.None;
|
|
|
|
private bool _showReporting;
|
|
|
|
private bool ShowReporting {
|
|
get => this._showReporting;
|
|
set => this._showReporting = value;
|
|
}
|
|
|
|
internal Message? ToShowModal { get; set; }
|
|
|
|
public Report(Plugin plugin) {
|
|
this.Plugin = plugin;
|
|
}
|
|
|
|
public void Open() {
|
|
this.ShowReporting = true;
|
|
}
|
|
|
|
public void Toggle() {
|
|
this.ShowReporting = !this.ShowReporting;
|
|
}
|
|
|
|
public void Draw() {
|
|
var toShow = this.ToShowModal;
|
|
if (toShow != null) {
|
|
if (!this.SetUpReportModal(toShow)) {
|
|
ImGui.OpenPopup($"###modal-message-{toShow.Id}");
|
|
}
|
|
}
|
|
|
|
if (!this.ShowReporting) {
|
|
return;
|
|
}
|
|
|
|
ImGui.SetNextWindowSize(new Vector2(1_000, 350), ImGuiCond.FirstUseEver);
|
|
|
|
var windowTitle = string.Format(Language.Reporting, this.Plugin.Name);
|
|
if (!ImGui.Begin($"{windowTitle}###NoSoliciting reporting", ref this._showReporting)) {
|
|
ImGui.End();
|
|
return;
|
|
}
|
|
|
|
ImGui.TextUnformatted(Language.ReportHelp);
|
|
|
|
if (this.LastReportStatus != ReportStatus.None) {
|
|
var status = this.LastReportStatus switch {
|
|
ReportStatus.Failure => Language.ReportStatusFailure,
|
|
ReportStatus.Successful => Language.ReportStatusSuccessful,
|
|
ReportStatus.InProgress => Language.ReportStatusInProgress,
|
|
_ => Language.ReportStatusUnknown,
|
|
};
|
|
var reportStatus = Language.ReportStatusMessage;
|
|
ImGui.TextUnformatted(string.Format(reportStatus, status));
|
|
}
|
|
|
|
ImGui.Separator();
|
|
ImGui.Spacing();
|
|
|
|
if (ImGui.BeginTabBar("##report-tabs")) {
|
|
this.ChatTab();
|
|
this.PartyFinderTab();
|
|
|
|
ImGui.EndTabBar();
|
|
}
|
|
|
|
ImGui.End();
|
|
}
|
|
|
|
private void ChatTab() {
|
|
if (!ImGui.BeginTabItem($"{Language.ReportChatTab}###chat-report")) {
|
|
return;
|
|
}
|
|
|
|
if (ImGui.BeginChild("##chat-messages", new Vector2(-1, -1))) {
|
|
if (ImGui.BeginTable("nosol-chat-report-table", 5, TableFlags)) {
|
|
ImGui.TableSetupColumn(Language.ReportColumnTimestamp);
|
|
ImGui.TableSetupColumn(Language.ReportColumnChannel);
|
|
ImGui.TableSetupColumn(Language.ReportColumnReason);
|
|
ImGui.TableSetupColumn(Language.ReportColumnSender);
|
|
ImGui.TableSetupColumn(Language.ReportColumnMessage, ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupScrollFreeze(0, 1);
|
|
ImGui.TableHeadersRow();
|
|
|
|
foreach (var message in this.Plugin.MessageHistory) {
|
|
ImGui.TableNextRow();
|
|
|
|
if (message.Filtered) {
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
|
|
}
|
|
|
|
var sender = message.Sender.Payloads
|
|
.Where(payload => payload.Type == PayloadType.RawText)
|
|
.Cast<TextPayload>()
|
|
.Select(payload => payload.Text)
|
|
.FirstOrDefault() ?? "";
|
|
|
|
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}");
|
|
}
|
|
|
|
if (message.Filtered) {
|
|
ImGui.PopStyleColor();
|
|
}
|
|
|
|
this.SetUpReportModal(message);
|
|
}
|
|
|
|
ImGui.EndTable();
|
|
}
|
|
|
|
ImGui.EndChild();
|
|
}
|
|
|
|
ImGui.EndTabItem();
|
|
}
|
|
|
|
private void PartyFinderTab() {
|
|
if (!ImGui.BeginTabItem($"{Language.ReportPartyFinderTab}###pf-report")) {
|
|
return;
|
|
}
|
|
|
|
#if DEBUG
|
|
if (ImGui.Button("Copy CSV")) {
|
|
var builder = new StringBuilder();
|
|
|
|
foreach (var message in this.Plugin.PartyFinderHistory) {
|
|
if (message.Classification == null) {
|
|
continue;
|
|
}
|
|
|
|
message.ToCsv(builder).Append('\n');
|
|
}
|
|
|
|
ImGui.SetClipboardText(builder.ToString());
|
|
}
|
|
#endif
|
|
|
|
if (ImGui.BeginChild("##pf-messages", new Vector2(-1, -1))) {
|
|
if (ImGui.BeginTable("nosol-pf-report-table", 4, TableFlags)) {
|
|
ImGui.TableSetupColumn(Language.ReportColumnTimestamp);
|
|
ImGui.TableSetupColumn(Language.ReportColumnReason);
|
|
ImGui.TableSetupColumn(Language.ReportColumnHost);
|
|
ImGui.TableSetupColumn(Language.ReportColumnDescription, ImGuiTableColumnFlags.WidthStretch);
|
|
ImGui.TableSetupScrollFreeze(0, 1);
|
|
ImGui.TableHeadersRow();
|
|
|
|
foreach (var message in this.Plugin.PartyFinderHistory) {
|
|
ImGui.TableNextRow();
|
|
|
|
if (message.Filtered) {
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(238f / 255f, 71f / 255f, 71f / 255f, 1f));
|
|
}
|
|
|
|
var sender = message.Sender.Payloads
|
|
.Where(payload => payload.Type == PayloadType.RawText)
|
|
.Cast<TextPayload>()
|
|
.Select(payload => payload.Text)
|
|
.FirstOrDefault() ?? "";
|
|
|
|
if (AddRow(message.Timestamp.ToString(CultureInfo.CurrentCulture), message.FilterReason ?? string.Empty, sender, message.Content.TextValue)) {
|
|
ImGui.OpenPopup($"###modal-message-{message.Id}");
|
|
}
|
|
|
|
if (message.Filtered) {
|
|
ImGui.PopStyleColor();
|
|
}
|
|
|
|
this.SetUpReportModal(message);
|
|
}
|
|
|
|
ImGui.EndTable();
|
|
}
|
|
|
|
ImGui.EndChild();
|
|
}
|
|
|
|
ImGui.EndTabItem();
|
|
}
|
|
|
|
#region Modal
|
|
|
|
private MessageCategory? _reportCategory;
|
|
private bool _other;
|
|
|
|
/// <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 false;
|
|
}
|
|
|
|
if (this._reportCategory == null && !this._other) {
|
|
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.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);
|
|
var preview = this._reportCategory?.Name() ?? (this._other ? Language.ReportModalClassificationOther : string.Empty);
|
|
if (ImGui.BeginCombo($"##modal-classification-{message.Id}", preview)) {
|
|
foreach (var category in (MessageCategory[]) Enum.GetValues(typeof(MessageCategory))) {
|
|
if (ImGui.Selectable($"{category.Name()}##modal-option-{message.Id}", this._reportCategory == category)) {
|
|
this._reportCategory = category;
|
|
this._other = false;
|
|
}
|
|
|
|
WrappedTooltip(category.Description());
|
|
}
|
|
|
|
if (ImGui.Selectable(Language.ReportModalClassificationOther)) {
|
|
this._reportCategory = null;
|
|
this._other = true;
|
|
}
|
|
|
|
WrappedTooltip(Language.ReportModalClassificationOtherDescription);
|
|
|
|
ImGui.EndCombo();
|
|
}
|
|
|
|
ImGui.Separator();
|
|
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
|
|
ImGui.TextUnformatted(Language.ReportModalHelp2);
|
|
ImGui.PopStyleColor();
|
|
|
|
ImGui.Separator();
|
|
|
|
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 (!message.EnabledSnapshot.WasEnabled(this._reportCategory ?? MessageCategory.Normal)) {
|
|
errorText = Language.ReportModalDisabledFilterNotEnabled;
|
|
} else if (this._reportCategory == (message.Classification ?? MessageCategory.Normal)) {
|
|
errorText = Language.ReportModalDisabledSameClassification;
|
|
}
|
|
|
|
if (this._other) {
|
|
if (ImGui.Button($"{Language.ReportModalGoToCustomButton}##report-goto-custom-{message.Id}")) {
|
|
ImGui.CloseCurrentPopup();
|
|
closing = true;
|
|
if (message == this.ToShowModal) {
|
|
this.ToShowModal = null;
|
|
}
|
|
|
|
this.Plugin.Ui.Settings.ShowOtherFilters = true;
|
|
this.Plugin.Ui.Settings.Show();
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
} else if (errorText != null) {
|
|
ImGui.PushStyleColor(ImGuiCol.Text, new Vector4(1f, 0f, 0f, 1f));
|
|
ImGui.TextUnformatted(errorText);
|
|
ImGui.PopStyleColor();
|
|
} else {
|
|
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.ToastGui.ShowNormal(string.Format(msg, message.Sender));
|
|
break;
|
|
}
|
|
case ReportStatus.Failure: {
|
|
var msg = Language.ReportToastFailure;
|
|
this.Plugin.ToastGui.ShowError(string.Format(msg, message.Sender));
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
this.ToShowModal = null;
|
|
} else {
|
|
this.ReportMessage(message, suggested);
|
|
}
|
|
|
|
ImGui.CloseCurrentPopup();
|
|
closing = true;
|
|
}
|
|
|
|
ImGui.SameLine();
|
|
}
|
|
|
|
if (ImGui.Button($"{Language.ReportModalCopy}##report-copy-{message.Id}")) {
|
|
ImGui.SetClipboardText(message.Content.TextValue);
|
|
}
|
|
|
|
#if DEBUG
|
|
ImGui.SameLine();
|
|
if (ImGui.Button("Copy CSV")) {
|
|
ImGui.SetClipboardText(message.ToCsv().ToString());
|
|
}
|
|
#endif
|
|
|
|
ImGui.SameLine();
|
|
|
|
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();
|
|
|
|
if (closing) {
|
|
this._other = false;
|
|
}
|
|
|
|
return closing;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Utility
|
|
|
|
private static bool AddRow(params string[] args) {
|
|
var clicked = false;
|
|
|
|
for (var i = 0; i < args.Length; i++) {
|
|
ImGui.TableNextColumn();
|
|
|
|
var arg = args[i];
|
|
var last = i == args.Length - 1;
|
|
|
|
if (last) {
|
|
ImGui.PushTextWrapPos();
|
|
}
|
|
|
|
ImGui.TextUnformatted(arg);
|
|
|
|
if (last) {
|
|
ImGui.PopTextWrapPos();
|
|
}
|
|
|
|
clicked = clicked || ImGui.IsItemClicked();
|
|
}
|
|
|
|
return clicked;
|
|
}
|
|
|
|
private static void WrappedTooltip(string text) {
|
|
if (!ImGui.IsItemHovered()) {
|
|
return;
|
|
}
|
|
|
|
ImGui.BeginTooltip();
|
|
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 24);
|
|
ImGui.TextUnformatted(text);
|
|
ImGui.PopTextWrapPos();
|
|
ImGui.EndTooltip();
|
|
}
|
|
|
|
private void ReportMessage(Message message, string suggested) {
|
|
Task.Run(async () => await this.ReportMessageAsync(message, suggested));
|
|
}
|
|
|
|
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) {
|
|
var json = message.ToJson(suggested);
|
|
if (json != null) {
|
|
resp = await client.UploadStringTaskAsync(reportUrl, json).ConfigureAwait(true);
|
|
}
|
|
}
|
|
} catch (Exception) {
|
|
// ignored
|
|
}
|
|
|
|
var status = resp == "{\"message\":\"ok\"}" ? ReportStatus.Successful : ReportStatus.Failure;
|
|
if (status == ReportStatus.Failure) {
|
|
PluginLog.LogWarning($"Failed to report message:\n{resp}");
|
|
}
|
|
|
|
this.LastReportStatus = status;
|
|
PluginLog.Log(resp == null
|
|
? "Report not sent. ML model not set."
|
|
: $"Report sent. Response: {resp}");
|
|
|
|
return status;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|