Add who command.
This commit is contained in:
parent
29b02407fa
commit
2053c9cbb3
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -131,6 +131,7 @@ dependencies = [
|
|||||||
"deadpool",
|
"deadpool",
|
||||||
"deadpool-postgres",
|
"deadpool-postgres",
|
||||||
"futures",
|
"futures",
|
||||||
|
"humantime",
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
@ -683,6 +684,12 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.23"
|
version = "0.14.23"
|
||||||
|
@ -37,3 +37,4 @@ once_cell = "1.16.0"
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
async-recursion = "1.0.0"
|
async-recursion = "1.0.0"
|
||||||
rand_distr = "0.4.3"
|
rand_distr = "0.4.3"
|
||||||
|
humantime = "2.1.0"
|
||||||
|
@ -18,8 +18,10 @@ use crate::models::{
|
|||||||
use tokio_postgres::types::ToSql;
|
use tokio_postgres::types::ToSql;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
use serde_json::{self, Value};
|
use serde_json::{self, Value};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DBPool {
|
pub struct DBPool {
|
||||||
@ -53,6 +55,12 @@ impl From<Row> for SendqueueItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct OnlineInfo {
|
||||||
|
pub username: String,
|
||||||
|
pub time: Option<DateTime<Utc>>
|
||||||
|
}
|
||||||
|
|
||||||
impl DBPool {
|
impl DBPool {
|
||||||
pub async fn record_listener_ping(self: &DBPool, listener: Uuid) -> DResult<()> {
|
pub async fn record_listener_ping(self: &DBPool, listener: Uuid) -> DResult<()> {
|
||||||
self.get_conn().await?.execute(
|
self.get_conn().await?.execute(
|
||||||
@ -176,6 +184,14 @@ impl DBPool {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn bump_session_time(&self, session: &ListenerSession) -> DResult<()> {
|
||||||
|
self.get_conn().await?.query(
|
||||||
|
"UPDATE sessions SET details=JSONB_SET(details, '{last_active}', to_json(NOW())::jsonb) \
|
||||||
|
WHERE session = $1", &[&session.session]
|
||||||
|
).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_conn(self: &DBPool) ->
|
pub async fn get_conn(self: &DBPool) ->
|
||||||
DResult<Object> {
|
DResult<Object> {
|
||||||
let conn = self.pool.get().await?;
|
let conn = self.pool.get().await?;
|
||||||
@ -591,6 +607,20 @@ impl DBTrans {
|
|||||||
pub async fn alloc_item_code(&self) -> DResult<i64> {
|
pub async fn alloc_item_code(&self) -> DResult<i64> {
|
||||||
Ok(self.pg_trans()?.query_one("SELECT NEXTVAL('item_seq')", &[]).await?.get(0))
|
Ok(self.pg_trans()?.query_one("SELECT NEXTVAL('item_seq')", &[]).await?.get(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_online_info(&self) ->DResult<Vec<OnlineInfo>> {
|
||||||
|
Ok(self.pg_trans()?.query(
|
||||||
|
"SELECT jsonb_build_object(\
|
||||||
|
'username', u.details->>'username',\
|
||||||
|
'time', s.details->>'last_active'\
|
||||||
|
) FROM sessions s \
|
||||||
|
JOIN users u ON u.current_session = s.session \
|
||||||
|
ORDER BY s.details->>'last_active' DESC", &[]
|
||||||
|
).await?
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|row| serde_json::from_value(row.get(0)).ok())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn commit(mut self: Self) -> DResult<()> {
|
pub async fn commit(mut self: Self) -> DResult<()> {
|
||||||
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
|
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
|
||||||
|
@ -24,6 +24,7 @@ mod quit;
|
|||||||
mod register;
|
mod register;
|
||||||
pub mod say;
|
pub mod say;
|
||||||
mod whisper;
|
mod whisper;
|
||||||
|
mod who;
|
||||||
|
|
||||||
pub struct VerbContext<'l> {
|
pub struct VerbContext<'l> {
|
||||||
pub session: &'l ListenerSession,
|
pub session: &'l ListenerSession,
|
||||||
@ -114,6 +115,8 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
|||||||
"-" => whisper::VERB,
|
"-" => whisper::VERB,
|
||||||
"whisper" => whisper::VERB,
|
"whisper" => whisper::VERB,
|
||||||
"tell" => whisper::VERB,
|
"tell" => whisper::VERB,
|
||||||
|
|
||||||
|
"who" => who::VERB,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
|
fn resolve_handler(ctx: &VerbContext, cmd: &str) -> Option<&'static UserVerbRef> {
|
||||||
@ -173,6 +176,7 @@ pub async fn handle(session: &ListenerSession, msg: &str, pool: &DBPool) -> DRes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pool.bump_session_time(&session).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
31
blastmud_game/src/message_handler/user_commands/who.rs
Normal file
31
blastmud_game/src/message_handler/user_commands/who.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use super::{VerbContext, UserVerb, UserVerbRef, UResult};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use ansi::{ignore_special_characters, ansi};
|
||||||
|
use chrono::Utc;
|
||||||
|
|
||||||
|
pub struct Verb;
|
||||||
|
#[async_trait]
|
||||||
|
impl UserVerb for Verb {
|
||||||
|
async fn handle(self: &Self, ctx: &mut VerbContext, _verb: &str, _remaining: &str) -> UResult<()> {
|
||||||
|
let mut msg = String::new();
|
||||||
|
msg.push_str(&format!(ansi!("<bold><bgblue><white>| {:20} | {:15} |<reset>\n"),
|
||||||
|
ansi!("Username"),
|
||||||
|
ansi!("Idle")));
|
||||||
|
for online in ctx.trans.get_online_info().await? {
|
||||||
|
if let Some(online_time) = online.time {
|
||||||
|
let diff =
|
||||||
|
humantime::format_duration(
|
||||||
|
std::time::Duration::from_secs(
|
||||||
|
(Utc::now() - online_time).num_seconds() as u64));
|
||||||
|
msg.push_str(&format!(
|
||||||
|
"| {:20} | {:15} |\n", &ignore_special_characters(&online.username),
|
||||||
|
&format!("{}", &diff)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg.push_str("\n");
|
||||||
|
ctx.trans.queue_for_session(ctx.session, Some(&msg)).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static VERB_INT: Verb = Verb;
|
||||||
|
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -1,6 +1,7 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use crate::regular_tasks::queued_command::QueueCommand;
|
use crate::regular_tasks::queued_command::QueueCommand;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -8,6 +9,7 @@ pub struct Session {
|
|||||||
pub source: String,
|
pub source: String,
|
||||||
pub less_explicit_mode: bool,
|
pub less_explicit_mode: bool,
|
||||||
pub queue: VecDeque<QueueCommand>,
|
pub queue: VecDeque<QueueCommand>,
|
||||||
|
pub last_active: Option<DateTime<Utc>>,
|
||||||
// Reminder: Consider backwards compatibility when updating this. New fields should generally
|
// Reminder: Consider backwards compatibility when updating this. New fields should generally
|
||||||
// be an Option, or you should ensure the default value is sensible, or things will
|
// be an Option, or you should ensure the default value is sensible, or things will
|
||||||
// crash out for existing sessions.
|
// crash out for existing sessions.
|
||||||
@ -26,6 +28,6 @@ impl Session {
|
|||||||
impl Default for Session {
|
impl Default for Session {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Session { source: "unknown".to_owned(), less_explicit_mode: false,
|
Session { source: "unknown".to_owned(), less_explicit_mode: false,
|
||||||
queue: VecDeque::new() }
|
queue: VecDeque::new(), last_active: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user