Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #18

Merged
merged 9 commits into from
Apr 23, 2024
Merged

Dev #18

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
name: cd
on:
push:
branches:
- master
workflow_run:
workflows: ["ci"]
types:
- completed
workflow_dispatch:
branches:
- 'master'


jobs:
deploy:
Expand Down
2 changes: 1 addition & 1 deletion renderer/template/leaderboard.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
<body>
<div class="main">
<section class="global-section">
<div class="warship">{{ship.tier}} {{ship.name}}</div>
<div class="warship">{{ship.tier_roman}} {{ship.name}}</div>
<img src='{{ship.icon}}'>
</section>
<div class="divider"></div>
Expand Down
2 changes: 1 addition & 1 deletion renderer/template/recent.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@
</div>
{{#ships}}
<div class="ship-table-row">
<div class="per-ship-tier ship-storo">{{info.tier}}</div>
<div class="per-ship-tier ship-storo">{{info.tier_roman}}</div>
<div class="per-ship-name ship-storo">{{info.short_name}}</div>
<div class="per-ship-battles ship-storo">{{stats.battles}}</div>
<div class="per-ship-exp ship-storo" style="color: {{stats.exp.color}};">{{stats.exp.value}}</div>
Expand Down
2 changes: 1 addition & 1 deletion renderer/template/single_ship.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@
<span class="region-box">{{user.region}}</span>
</div>
<div class="global-title">
<span class="ship-name">{{ship.tier}} {{ship.name}} {{suffix}}</span>
<span class="ship-name">{{ship.tier_roman}} {{ship.name}} {{suffix}}</span>
{{#ranking}}
<span class="ranking-box">#{{ranking}}</span>
{{/ranking}}
Expand Down
18 changes: 17 additions & 1 deletion src/cmds/owner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::dc_utils::Args;
use crate::dc_utils::{Args, ContextAddon};
use crate::utils::wws_api::WowsApi;
use crate::utils::LoadSaveFromJson;
use crate::{Context, Error};
use poise::serenity_prelude::{ArgumentConvert, Channel, ReactionType};
Expand All @@ -19,6 +20,21 @@ pub async fn clan_season(ctx: Context<'_>, season: u32) -> Result<(), Error> {
Ok(())
}

#[poise::command(prefix_command, owners_only, hide_in_help)]
pub async fn update_src(ctx: Context<'_>) -> Result<(), Error> {
let _typing = ctx.typing().await;
let api = WowsApi::new(&ctx);
if let Ok(res) = api.encyclopedia_vehicles().await {
res.save_json().await;
*ctx.data().ship_js.write() = res;
}
let res = api.encyclopedia_vehicles().await?;
res.save_json().await;
*ctx.data().ship_js.write() = res;
ctx.reply("Updated").await?;
Ok(())
}

#[poise::command(prefix_command, owners_only, hide_in_help)]
pub async fn guilds(ctx: Context<'_>) -> Result<(), Error> {
let _cache = ctx.cache();
Expand Down
24 changes: 13 additions & 11 deletions src/cmds/recent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
},
utils::{
structs::{
Mode, PartialPlayer, Player, RecentPlayer, RecentPlayerType, Ship, ShipId,
Mode, PartialPlayer, Player, PlayerSnapshots, PlayerSnapshotsType, Ship, ShipId,
ShipModeStatsPair, ShipStatsCollection,
},
wws_api::WowsApi,
Expand Down Expand Up @@ -286,28 +286,30 @@ async fn func_recent(
async fn load_player(
player: &Player,
curren_ships: &ShipStatsCollection,
// QA 打包成一個struct會比較好嗎? (下面RecentPlayerLoadResult), 但還是得攤開,而且攤開可以利用unused強迫處理
) -> (bool, bool, RecentPlayer) {
// QA 打包成一個struct會比較好嗎? (下面PlayerSnapshotsLoadResult), 但還是得攤開,而且攤開可以利用unused強迫處理
) -> (bool, bool, PlayerSnapshots) {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();

let last_request = if player.premium {
RecentPlayerType::Premium
PlayerSnapshotsType::Premium
} else {
RecentPlayerType::Normal(now)
PlayerSnapshotsType::Normal(now)
};
let (is_new, mut player_data) =
if let Some(player_data) = RecentPlayer::load(&player.partial_player).await {
if let Some(player_data) = PlayerSnapshots::load(&player.partial_player).await {
(false, player_data)
} else {
let player_data = RecentPlayer::init(&player.partial_player).await;
let player_data = PlayerSnapshots::init(&player.partial_player).await;
(true, player_data)
};
let is_active = match player_data.last_request {
RecentPlayerType::Premium => true,
RecentPlayerType::Normal(timestamp) => now - timestamp < RECENT_LAST_REQUEST_LIMIT * 86400,
PlayerSnapshotsType::Premium => true,
PlayerSnapshotsType::Normal(timestamp) => {
now - timestamp < RECENT_LAST_REQUEST_LIMIT * 86400
}
};
// update the last_requst timestamp
player_data.last_request = last_request;
Expand All @@ -320,10 +322,10 @@ async fn load_player(
(is_new, is_active, player_data)
}

// pub struct RecentPlayerLoadResult {
// pub struct PlayerSnapshotsLoadResult {
// pub is_new: bool, // if its just init
// pub is_active: bool, // if ISAC is still tracking
// pub data: RecentPlayer,
// pub data: PlayerSnapshots,
// }

pub struct AskDay<'a> {
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ async fn main() {
owner::send(),
owner::users(),
owner::clan_season(),
owner::update_src(),
tools::roulette(),
tools::rename(),
tools::history(),
Expand Down
44 changes: 12 additions & 32 deletions src/utils/structs/recent.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,20 @@
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::io::AsyncWriteExt;

use crate::utils::structs::{PartialPlayer, ShipStatsCollection};

#[derive(Debug, Serialize, Deserialize)]
pub struct RecentPlayer {
pub struct PlayerSnapshots {
#[serde(default, skip_serializing)]
pub player: PartialPlayer,
pub last_update_at: u64, // unix timestamp
#[serde(deserialize_with = "last_request_migrate")]
pub last_request: RecentPlayerType,
pub last_request: PlayerSnapshotsType,
pub data: BTreeMap<u64, ShipStatsCollection>,
}

fn last_request_migrate<'de, D>(deserializer: D) -> Result<RecentPlayerType, D::Error>
where
D: serde::Deserializer<'de>,
{
let v: Value = Deserialize::deserialize(deserializer)?;
Ok(
if let Ok(last_request) = serde_json::from_value::<RecentPlayerType>(v.clone()) {
last_request
} else {
RecentPlayerType::Normal(v.as_u64().unwrap_or_default())
},
)
}

impl RecentPlayer {
impl PlayerSnapshots {
/// try to get the given date data first, then find the closest one, None if no matched
pub async fn get_date(&self, timestamp: &u64) -> Option<(u64, ShipStatsCollection)> {
if let Some((k, v)) = self.data.get_key_value(timestamp) {
Expand All @@ -53,13 +37,9 @@ impl RecentPlayer {
}
/// load the player's recent data, return None if he is not inside
pub async fn load(player: &PartialPlayer) -> Option<Self> {
let path = format!(
"./recent_DB/players/{}/{}.json",
player.region.lower(),
player.uid
);
let path = Self::get_path(player);
if let Ok(file) = std::fs::File::open(&path) {
let mut data: RecentPlayer = tokio::task::spawn_blocking(move || {
let mut data: PlayerSnapshots = tokio::task::spawn_blocking(move || {
let reader = std::io::BufReader::new(file);
serde_json::from_reader(reader)
})
Expand All @@ -78,14 +58,14 @@ impl RecentPlayer {
Self {
player: *player,
last_update_at: 0,
last_request: RecentPlayerType::Normal(0),
last_request: PlayerSnapshotsType::Normal(0),
data: Default::default(),
}
}

/// save player data
pub async fn save(&self) {
let path = self.get_path();
let path = Self::get_path(&self.player);

let mut file = tokio::fs::File::create(&path)
.await
Expand All @@ -102,21 +82,21 @@ impl RecentPlayer {
}

/// get player's file path
fn get_path(&self) -> String {
fn get_path(player: &PartialPlayer) -> String {
format!(
"./recent_DB/players/{}/{}.json",
self.player.region.lower(),
self.player.uid
player.region.lower(),
player.uid
)
}
}

#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum RecentPlayerType {
pub enum PlayerSnapshotsType {
#[serde(alias = "prime")]
Premium,
Normal(u64),
}

// #[derive(Debug, Serialize, Deserialize)]
// pub struct RecentPlayerData { }
// pub struct PlayerSnapshotsData { }
68 changes: 64 additions & 4 deletions src/utils/structs/ship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::{collections::HashMap, fmt::Display, sync::Arc};
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use serde_repr::Deserialize_repr;
use strum::{EnumIter, IntoEnumIterator};
use serde_repr::{Deserialize_repr, Serialize_repr};
use strum::{Display, EnumIter, IntoEnumIterator};

use crate::{
utils::{
Expand All @@ -28,8 +28,30 @@ pub enum ShipClass {
CV,
}

impl ShipClass {
/// convert tag to ship class, None if no matched
///
/// Destroyer => [`ShipClass::DD`]
/// Cruiser => [`ShipClass::CA`]
/// Battleship => [`ShipClass::BB`]
/// AirCarrier => [`ShipClass::CV`]
/// Submarine => [`ShipClass::SS`]
pub fn from_tag(input: impl AsRef<str>) -> Option<Self> {
match input.as_ref() {
"Destroyer" => Some(Self::DD),
"Cruiser" => Some(Self::CA),
"Battleship" => Some(Self::BB),
"AirCarrier" => Some(Self::CV),
"Submarine" => Some(Self::SS),
_ => None,
}
}
}

#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, Deserialize_repr, Serialize, EnumIter, PartialEq, Eq, Hash)]
#[derive(
Debug, Clone, Copy, Deserialize_repr, Serialize_repr, EnumIter, PartialEq, Eq, Hash, Display,
)]
#[repr(u8)]
pub enum ShipTier {
I = 1,
Expand All @@ -45,6 +67,42 @@ pub enum ShipTier {
XI = 11,
}

impl From<ShipTier> for ShipTierRoman {
fn from(value: ShipTier) -> Self {
match value {
ShipTier::I => Self::I,
ShipTier::II => Self::II,
ShipTier::III => Self::III,
ShipTier::IV => Self::IV,
ShipTier::V => Self::V,
ShipTier::VI => Self::VI,
ShipTier::VII => Self::VII,
ShipTier::VIII => Self::VIII,
ShipTier::IX => Self::IX,
ShipTier::X => Self::X,
ShipTier::XI => Self::XI,
}
}
}

/// Should use [`ShipTier`] most of the time, this enum is for display for user
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub enum ShipTierRoman {
I,
II,
III,
IV,
V,
VI,
VII,
VIII,
IX,
X,
#[serde(rename = "★")]
XI,
}

fn add_glossary_url_prefix<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: serde::Deserializer<'de>,
Expand All @@ -53,10 +111,11 @@ where
Ok(format!("https://wows-gloss-icons.wgcdn.co/icons/{}", s))
}

#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Ship {
pub ship_id: ShipId,
pub tier: ShipTier,
pub tier_roman: ShipTierRoman,
pub class: ShipClass,
pub name: String,
pub short_name: String,
Expand All @@ -70,6 +129,7 @@ impl Default for Ship {
Self {
ship_id: ShipId(0),
tier: ShipTier::X,
tier_roman: ShipTierRoman::X,
class: ShipClass::DD,
name: "Unknown Ship".to_string(),
short_name: "Unknown Ship".to_string(),
Expand Down
Loading
Loading