195 lines
6.4 KiB
C#
195 lines
6.4 KiB
C#
using System.Collections;
|
|
using System.Net.Http.Headers;
|
|
using Dalamud.Game.ClientState.Objects.Enums;
|
|
using EorzeaVotes.Model;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace EorzeaVotes;
|
|
|
|
internal class QuestionManager : IDisposable, IReadOnlyList<IQuestion> {
|
|
private Plugin Plugin { get; }
|
|
private HttpClient Http { get; }
|
|
|
|
private QuestionsResponse _questions = new() {
|
|
Page = [],
|
|
};
|
|
|
|
private Guid _lastSeenActive = Guid.Empty;
|
|
private int _page = 1;
|
|
|
|
internal QuestionManager(Plugin plugin) {
|
|
this.Plugin = plugin;
|
|
this.Http = new HttpClient {
|
|
BaseAddress = new Uri("https://ev.anna.lgbt/"),
|
|
DefaultRequestHeaders = {
|
|
UserAgent = {
|
|
new ProductInfoHeaderValue("EorzeaVotes", typeof(QuestionManager).Assembly.GetName().Version?.ToString(3) ?? "???"),
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
public void Dispose() {
|
|
this.Http.Dispose();
|
|
}
|
|
|
|
internal IQuestion? Current => this._questions.Current;
|
|
|
|
internal bool HasNext => this._questions.HasNext;
|
|
|
|
internal bool Loading { get; private set; }
|
|
|
|
internal int Page {
|
|
get => this._page;
|
|
set {
|
|
if (this.Loading) {
|
|
throw new InvalidOperationException("cannot set page while still loading");
|
|
}
|
|
|
|
var old = this._page;
|
|
|
|
this.Loading = true;
|
|
this._page = Math.Max(1, value);
|
|
|
|
Task.Run(async () => {
|
|
try {
|
|
await this.Check();
|
|
} catch {
|
|
this._page = old;
|
|
throw;
|
|
} finally {
|
|
this.Loading = false;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
public int Count => this._questions.Page.Count;
|
|
|
|
public IQuestion this[int index] => this._questions.Page[index];
|
|
|
|
public IEnumerator<IQuestion> GetEnumerator() {
|
|
return this._questions.Page.GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() {
|
|
return this.GetEnumerator();
|
|
}
|
|
|
|
private HttpRequestMessage MakeMessage(HttpMethod method, string url) {
|
|
return new HttpRequestMessage(method, url) {
|
|
Headers = {
|
|
{ "X-Api-Key", this.Plugin.Config.ApiKey },
|
|
},
|
|
};
|
|
}
|
|
|
|
internal async Task<bool> Vote(Guid questionId, ushort answer) {
|
|
var player = await this.Plugin.Framework.RunOnFrameworkThread(() => this.Plugin.ClientState.LocalPlayer);
|
|
|
|
var voteReq = new VoteRequest {
|
|
QuestionId = questionId,
|
|
Answer = answer,
|
|
Stats = new VoteStats {
|
|
Gender = this.Plugin.Config.Gender,
|
|
BirthDate = this.Plugin.Config.BirthDate,
|
|
CharacterRace = this.Plugin.Config.RaceClanGender
|
|
? player?.Customize[(int) CustomizeIndex.Race]
|
|
: null,
|
|
CharacterClan = this.Plugin.Config.RaceClanGender
|
|
? player?.Customize[(int) CustomizeIndex.Tribe]
|
|
: null,
|
|
CharacterGender = this.Plugin.Config.RaceClanGender
|
|
? player?.Customize[(int) CustomizeIndex.Gender]
|
|
: null,
|
|
CharacterHomeWorld = this.Plugin.Config.HomeWorld
|
|
? player?.HomeWorld.Id
|
|
: null,
|
|
YearStartedPlaying = (ushort?) this.Plugin.Config.YearStartedPlaying,
|
|
MyersBriggs = this.Plugin.Config.MyersBriggs,
|
|
MainJob = this.Plugin.Config.MainJob,
|
|
},
|
|
};
|
|
|
|
var req = this.MakeMessage(HttpMethod.Post, "/vote");
|
|
req.Content = new StringContent(JsonConvert.SerializeObject(voteReq, Plugin.SerializerSettings)) {
|
|
Headers = {
|
|
ContentType = new MediaTypeHeaderValue("application/json"),
|
|
},
|
|
};
|
|
|
|
var resp = await this.Http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
|
|
return resp.IsSuccessStatusCode;
|
|
}
|
|
|
|
private async Task RegisterIfNecessary() {
|
|
if (this.Plugin.Config is { UserId: not null, ApiKey: not null }) {
|
|
return;
|
|
}
|
|
|
|
var req = this.MakeMessage(HttpMethod.Post, "/user");
|
|
var resp = await this.Http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
|
|
var json = await resp.Content.ReadAsStringAsync();
|
|
var data = JsonConvert.DeserializeObject<RegisterResponse>(json, Plugin.SerializerSettings)!;
|
|
|
|
this.Plugin.Config.UserId = data.UserId;
|
|
this.Plugin.Config.ApiKey = data.ApiKey;
|
|
this.Plugin.SaveConfig();
|
|
}
|
|
|
|
[Serializable]
|
|
private class QuestionsResponse {
|
|
public IQuestion? Current { get; init; }
|
|
public required List<IQuestion> Page { get; init; }
|
|
public bool HasNext { get; init; }
|
|
}
|
|
|
|
internal async Task Check() {
|
|
await this.RegisterIfNecessary();
|
|
var req = this.MakeMessage(HttpMethod.Get, $"/questions?v=2&page={this._page}");
|
|
var resp = await this.Http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
|
|
var json = await resp.Content.ReadAsStringAsync();
|
|
this._questions = JsonConvert.DeserializeObject<QuestionsResponse>(json, Plugin.SerializerSettings) ?? new QuestionsResponse {
|
|
Current = null,
|
|
Page = [],
|
|
HasNext = false,
|
|
};
|
|
await this.OpenIfNew();
|
|
}
|
|
|
|
internal async Task OpenIfNew() {
|
|
if (!this.Plugin.Config.OnBoarded) {
|
|
return;
|
|
}
|
|
|
|
var isLoggedIn = await this.Plugin.Framework.RunOnFrameworkThread(() => this.Plugin.ClientState.IsLoggedIn);
|
|
if (!isLoggedIn) {
|
|
return;
|
|
}
|
|
|
|
var active = this.Current;
|
|
if (active is not BasicQuestion) {
|
|
return;
|
|
}
|
|
|
|
if (this._lastSeenActive != active.Id) {
|
|
this.Plugin.Ui.Visible = true;
|
|
}
|
|
|
|
this._lastSeenActive = active.Id;
|
|
}
|
|
|
|
internal async Task<Dictionary<string, ulong[]>> GetBreakdown(Guid question, Breakdown breakdown) {
|
|
var req = this.MakeMessage(
|
|
HttpMethod.Get,
|
|
$"/question/{question}/breakdown/{breakdown.ToSnakeCase()}"
|
|
);
|
|
|
|
var resp = await this.Http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead);
|
|
var json = await resp.Content.ReadAsStringAsync();
|
|
var data = JsonConvert.DeserializeObject<Dictionary<string, ulong[]>>(json)!;
|
|
|
|
return data;
|
|
}
|
|
}
|