pinger? I hardly know 'er!
This commit is contained in:
parent
07efd7f71a
commit
54d56eb194
|
@ -58,17 +58,17 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.8"/>
|
||||
<PackageReference Include="Fody" Version="6.6.3" PrivateAssets="all"/>
|
||||
<PackageReference Include="Resourcer.Fody" Version="1.8.0" PrivateAssets="all"/>
|
||||
<PackageReference Include="YamlDotNet" Version="12.0.0"/>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.8" />
|
||||
<PackageReference Include="Fody" Version="6.6.3" PrivateAssets="all" />
|
||||
<PackageReference Include="Resourcer.Fody" Version="1.8.0" PrivateAssets="all" />
|
||||
<PackageReference Include="YamlDotNet" Version="12.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="../server/packs/*.yaml" LinkBase="packs"/>
|
||||
<EmbeddedResource Remove="../server/packs/*_old*.yaml"/>
|
||||
<EmbeddedResource Include="vfx/b0941trp1*_o.avfx"/>
|
||||
<EmbeddedResource Include="img/sign_*.jpg"/>
|
||||
<EmbeddedResource Include="../server/packs/*.yaml" LinkBase="packs" />
|
||||
<EmbeddedResource Remove="../server/packs/*_old*.yaml" />
|
||||
<EmbeddedResource Include="vfx/b0941trp1*_o.avfx" />
|
||||
<EmbeddedResource Include="img/sign_*.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
using System.Diagnostics;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Logging;
|
||||
using OrangeGuidanceTomestone.Helpers;
|
||||
|
||||
namespace OrangeGuidanceTomestone;
|
||||
|
||||
internal class Pinger : IDisposable {
|
||||
private Plugin Plugin { get; }
|
||||
private Stopwatch Stopwatch { get; } = new();
|
||||
private int _waitSecs;
|
||||
|
||||
internal Pinger(Plugin plugin) {
|
||||
this.Plugin = plugin;
|
||||
|
||||
this.Stopwatch.Start();
|
||||
|
||||
this.Plugin.Framework.Update += this.Ping;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Plugin.Framework.Update -= this.Ping;
|
||||
}
|
||||
|
||||
private void Ping(Framework framework) {
|
||||
if (this.Stopwatch.Elapsed < TimeSpan.FromSeconds(this._waitSecs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.Stopwatch.Restart();
|
||||
|
||||
if (this.Plugin.Config.ApiKey == string.Empty) {
|
||||
this._waitSecs = 5;
|
||||
return;
|
||||
}
|
||||
|
||||
// 30 mins
|
||||
this._waitSecs = 1_800;
|
||||
|
||||
Task.Run(async () => {
|
||||
var resp = await ServerHelper.SendRequest(
|
||||
this.Plugin.Config.ApiKey,
|
||||
HttpMethod.Post,
|
||||
"/ping"
|
||||
);
|
||||
|
||||
if (!resp.IsSuccessStatusCode) {
|
||||
PluginLog.LogWarning($"Failed to ping, status {resp.StatusCode}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ public class Plugin : IDalamudPlugin {
|
|||
internal Messages Messages { get; }
|
||||
internal VfxReplacer VfxReplacer { get; }
|
||||
internal Commands Commands { get; }
|
||||
internal Pinger Pinger { get; }
|
||||
|
||||
internal string AvfxFilePath { get; }
|
||||
|
||||
|
@ -52,6 +53,7 @@ public class Plugin : IDalamudPlugin {
|
|||
this.Ui = new PluginUi(this);
|
||||
this.VfxReplacer = new VfxReplacer(this);
|
||||
this.Commands = new Commands(this);
|
||||
this.Pinger = new Pinger(this);
|
||||
|
||||
if (this.Config.ApiKey == string.Empty) {
|
||||
this.GetApiKey();
|
||||
|
@ -59,6 +61,7 @@ public class Plugin : IDalamudPlugin {
|
|||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.Pinger.Dispose();
|
||||
this.Commands.Dispose();
|
||||
this.VfxReplacer.Dispose();
|
||||
this.Ui.Dispose();
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
alter table users
|
||||
add column last_seen timestamp not null default 0;
|
||||
|
||||
-- noinspection SqlWithoutWhere
|
||||
update users
|
||||
set last_seen = current_timestamp;
|
|
@ -19,7 +19,6 @@ mod message;
|
|||
mod web;
|
||||
mod util;
|
||||
mod config;
|
||||
mod task;
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use chrono::{Duration, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::types::chrono::NaiveDateTime;
|
||||
use uuid::Uuid;
|
||||
|
@ -45,13 +44,8 @@ pub struct RetrievedMessage {
|
|||
pub created: NaiveDateTime,
|
||||
#[serde(skip)]
|
||||
pub user: String,
|
||||
}
|
||||
|
||||
impl RetrievedMessage {
|
||||
pub fn adjusted_time_since_posting(&self) -> Duration {
|
||||
let score = (self.positive_votes - self.negative_votes).max(0);
|
||||
Utc::now().naive_utc().signed_duration_since(self.created) - Duration::weeks(score as i64)
|
||||
}
|
||||
#[serde(skip)]
|
||||
pub last_seen_minutes: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
mod delete_old;
|
|
@ -1,8 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
use crate::State;
|
||||
|
||||
pub fn delete_old(state: Arc<State>) {
|
||||
tokio::task::spawn(async move {
|
||||
|
||||
});
|
||||
}
|
|
@ -17,6 +17,7 @@ mod vote;
|
|||
mod get_mine;
|
||||
mod get_message;
|
||||
mod claim;
|
||||
mod ping;
|
||||
|
||||
pub fn routes(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
||||
register::register(Arc::clone(&state))
|
||||
|
@ -28,6 +29,7 @@ pub fn routes(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
|||
.or(get_location::get_location(Arc::clone(&state)))
|
||||
.or(get_mine::get_mine(Arc::clone(&state)))
|
||||
.or(claim::claim(Arc::clone(&state)))
|
||||
.or(ping::ping(Arc::clone(&state)))
|
||||
.recover(handle_rejection)
|
||||
.boxed()
|
||||
}
|
||||
|
|
|
@ -38,10 +38,12 @@ async fn logic(state: Arc<State>, id: i64, location: u32) -> Result<impl Reply,
|
|||
v2.vote as user_vote,
|
||||
m.glyph,
|
||||
m.created,
|
||||
m.user
|
||||
m.user,
|
||||
cast((julianday(current_timestamp) - julianday(created)) * 1440 as int) as last_seen_minutes
|
||||
from messages m
|
||||
left join votes v on m.id = v.message
|
||||
left join votes v2 on m.id = v2.message and v2.user = ?
|
||||
inner join users u on m.user = u.id
|
||||
where m.territory = ?
|
||||
group by m.id"#,
|
||||
id,
|
||||
|
@ -63,8 +65,17 @@ fn filter_messages(messages: &mut Vec<RetrievedMessage>, id: i64) {
|
|||
// just count nearby messages. this is O(n^2) but alternatives are hard
|
||||
let mut ids = Vec::with_capacity(messages.len());
|
||||
for a in messages.iter() {
|
||||
let mut nearby = 0;
|
||||
if a.user == id_str {
|
||||
// always include own messages
|
||||
ids.push(a.id.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
if a.last_seen_minutes >= 35 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut nearby = 0;
|
||||
for b in messages.iter() {
|
||||
if a.id == b.id {
|
||||
continue;
|
||||
|
@ -81,36 +92,39 @@ fn filter_messages(messages: &mut Vec<RetrievedMessage>, id: i64) {
|
|||
nearby += 1;
|
||||
}
|
||||
|
||||
if a.user == id_str || nearby <= 2 {
|
||||
// always include groups of three or fewer
|
||||
ids.push(a.id.clone());
|
||||
continue;
|
||||
}
|
||||
let (numerator, denominator) = if nearby <= 2 {
|
||||
// no need to do calculations for groups of three or fewer
|
||||
(17, 20)
|
||||
} else {
|
||||
let time_since_creation = a.created.signed_duration_since(Utc::now().naive_utc());
|
||||
let brand_new = time_since_creation < Duration::minutes(30);
|
||||
let new = time_since_creation < Duration::hours(2);
|
||||
|
||||
let score = (a.positive_votes - a.negative_votes).max(0);
|
||||
let time_since_creation = a.adjusted_time_since_posting();
|
||||
if time_since_creation > Duration::weeks(1) {
|
||||
continue;
|
||||
}
|
||||
let mut numerator = 1;
|
||||
if brand_new {
|
||||
numerator = nearby;
|
||||
} else if new {
|
||||
numerator += (nearby / 3).min(1);
|
||||
}
|
||||
|
||||
// originally thresholds were 6 hours and 2 days
|
||||
let brand_new = time_since_creation < Duration::minutes(30);
|
||||
let new = time_since_creation < Duration::hours(2);
|
||||
let score = (a.positive_votes - a.negative_votes).max(0);
|
||||
if score > 0 {
|
||||
let pad = score as f32 / nearby as f32;
|
||||
let rounded = pad.round() as u32;
|
||||
numerator += rounded.max(nearby / 2);
|
||||
}
|
||||
|
||||
let mut numerator = 1;
|
||||
if brand_new {
|
||||
numerator = nearby;
|
||||
} else if new {
|
||||
numerator += (nearby / 3).min(1);
|
||||
}
|
||||
nearby *= 2;
|
||||
|
||||
if score > 0 {
|
||||
let pad = score as f32 / nearby as f32;
|
||||
let rounded = pad.round() as u32;
|
||||
numerator += rounded.max(nearby / 2);
|
||||
}
|
||||
if numerator * 20 > nearby * 17 {
|
||||
numerator = 17;
|
||||
nearby = 20;
|
||||
}
|
||||
|
||||
if rand::thread_rng().gen_ratio(numerator.min(nearby), nearby * 2) {
|
||||
(numerator, nearby)
|
||||
};
|
||||
|
||||
if rand::thread_rng().gen_ratio(numerator.min(denominator), denominator) {
|
||||
ids.push(a.id.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use warp::{Filter, Rejection, Reply};
|
||||
use warp::filters::BoxedFilter;
|
||||
|
||||
use crate::State;
|
||||
use crate::web::AnyhowRejection;
|
||||
|
||||
pub fn ping(state: Arc<State>) -> BoxedFilter<(impl Reply, )> {
|
||||
warp::post()
|
||||
.and(warp::path("ping"))
|
||||
.and(warp::path::end())
|
||||
.and(super::get_id(Arc::clone(&state)))
|
||||
.and_then(move |(id, _)| logic(Arc::clone(&state), id))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
async fn logic(state: Arc<State>, id: i64) -> Result<impl Reply, Rejection> {
|
||||
sqlx::query!(
|
||||
// language=sqlite
|
||||
"update users set last_seen = current_timestamp where id = ?",
|
||||
id,
|
||||
)
|
||||
.execute(&state.db)
|
||||
.await
|
||||
.context("could not update user")
|
||||
.map_err(AnyhowRejection)
|
||||
.map_err(warp::reject::custom)?;
|
||||
|
||||
Ok(warp::reply())
|
||||
}
|
|
@ -21,7 +21,7 @@ async fn logic(state: Arc<State>) -> Result<impl Reply, Rejection> {
|
|||
let hashed = crate::util::hash(&auth);
|
||||
sqlx::query!(
|
||||
// language=sqlite
|
||||
"insert into users (auth) values (?)",
|
||||
"insert into users (auth, last_seen) values (?, current_timestamp)",
|
||||
hashed,
|
||||
)
|
||||
.execute(&state.db)
|
||||
|
|
Loading…
Reference in New Issue