diff options
| author | Jokler <jokler@protonmail.com> | 2020-02-03 01:14:05 +0100 |
|---|---|---|
| committer | Jokler <jokler@protonmail.com> | 2020-02-22 23:20:10 +0100 |
| commit | 5eea11a03c11551091b2c72f48590aec7f5410f0 (patch) | |
| tree | 4767841f341b82539c2586ef04e67fe72e52bd4b /src | |
| parent | 2831c2b60cb61a14c7efee4ab5c0389eb3ad5469 (diff) | |
| download | pokebot-5eea11a03c11551091b2c72f48590aec7f5410f0.tar.gz pokebot-5eea11a03c11551091b2c72f48590aec7f5410f0.zip | |
Add a json /api/bots endpoint for data retrieval
Diffstat (limited to 'src')
| -rw-r--r-- | src/bot/master.rs | 14 | ||||
| -rw-r--r-- | src/bot/music.rs | 3 | ||||
| -rw-r--r-- | src/web_server.rs | 90 |
3 files changed, 92 insertions, 15 deletions
diff --git a/src/bot/master.rs b/src/bot/master.rs index 10a7572..67867ef 100644 --- a/src/bot/master.rs +++ b/src/bot/master.rs @@ -213,6 +213,20 @@ impl MasterBot { Ok(()) } + pub fn bot_data(&self, name: String) -> Option<crate::web_server::BotData> { + let music_bots = self.music_bots.read().unwrap(); + + let bot = music_bots.connected_bots.get(&name)?; + + Some(crate::web_server::BotData { + name: name, + state: bot.state(), + volume: bot.volume(), + currently_playing: bot.currently_playing(), + playlist: bot.playlist_to_vec(), + }) + } + pub fn bot_datas(&self) -> Vec<crate::web_server::BotData> { let music_bots = self.music_bots.read().unwrap(); diff --git a/src/bot/music.rs b/src/bot/music.rs index d53e4a8..0def280 100644 --- a/src/bot/music.rs +++ b/src/bot/music.rs @@ -5,6 +5,7 @@ use std::thread; use humantime; use log::{debug, info}; +use serde::Serialize; use structopt::StructOpt; use tokio02::sync::mpsc::UnboundedSender; use tsclientlib::{data, ChannelId, ClientId, ConnectOptions, Identity, Invoker, MessageTarget}; @@ -44,7 +45,7 @@ fn parse_seek(mut amount: &str) -> Result<Seek, ()> { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize)] pub enum State { Playing, Paused, diff --git a/src/web_server.rs b/src/web_server.rs index 94f043a..02c57e7 100644 --- a/src/web_server.rs +++ b/src/web_server.rs @@ -1,9 +1,13 @@ use std::sync::Arc; use actix::{Actor, Addr, Handler, Message, SyncArbiter, SyncContext}; -use actix_web::{get, middleware::Logger, web, App, HttpServer, Responder}; +use actix_web::{ + get, middleware::Logger, web, App, HttpResponse, HttpServer, Responder, ResponseError, +}; use askama::actix_web::TemplateIntoResponse; use askama::Template; +use derive_more::Display; +use serde::Serialize; use crate::bot::MasterBot; use crate::youtube_dl::AudioMetadata; @@ -24,6 +28,7 @@ pub async fn start(args: WebServerArgs) -> std::io::Result<()> { .data(bot_addr.clone()) .wrap(Logger::default()) .service(index) + .service(web::scope("/api").service(get_bot_list).service(get_bot)) .service(actix_files::Files::new("/static", "static/")) }) .bind(args.bind_address)? @@ -41,29 +46,47 @@ impl Actor for BotExecutor { type Context = SyncContext<Self>; } -impl Handler<PlaylistRequest> for BotExecutor { +struct BotDataListRequest; + +impl Message for BotDataListRequest { + // A plain Vec does not work for some reason + type Result = Result<Vec<BotData>, ()>; +} + +impl Handler<BotDataListRequest> for BotExecutor { type Result = Result<Vec<BotData>, ()>; - fn handle(&mut self, _: PlaylistRequest, _: &mut Self::Context) -> Self::Result { + fn handle(&mut self, _: BotDataListRequest, _: &mut Self::Context) -> Self::Result { let bot = &self.0; Ok(bot.bot_datas()) } } -struct PlaylistRequest; +struct BotDataRequest(String); -impl Message for PlaylistRequest { - type Result = Result<Vec<BotData>, ()>; +impl Message for BotDataRequest { + type Result = Option<BotData>; +} + +impl Handler<BotDataRequest> for BotExecutor { + type Result = Option<BotData>; + + fn handle(&mut self, r: BotDataRequest, _: &mut Self::Context) -> Self::Result { + let name = r.0; + let bot = &self.0; + + bot.bot_data(name) + } } #[derive(Template)] #[template(path = "index.htm")] -struct PlaylistTemplate<'a> { +struct OverviewTemplate<'a> { bots: &'a [BotData], } -#[derive(Debug)] +#[derive(Debug, Serialize)] pub struct BotData { pub name: String, pub state: crate::bot::State, @@ -74,16 +97,55 @@ pub struct BotData { #[get("/")] async fn index(bot: web::Data<Addr<BotExecutor>>) -> impl Responder { - let bot_datas = match bot.send(PlaylistRequest).await.unwrap() { + let bot_datas = match bot.send(BotDataListRequest).await.unwrap() { Ok(data) => data, - Err(_) => { - //error!("Playlist error: {}", e); - Vec::with_capacity(0) - } + Err(_) => Vec::with_capacity(0), }; - PlaylistTemplate { + OverviewTemplate { bots: &bot_datas[..], } .into_response() } + +#[get("/bots")] +async fn get_bot_list(bot: web::Data<Addr<BotExecutor>>) -> impl Responder { + let bot_datas = match bot.send(BotDataListRequest).await.unwrap() { + Ok(data) => data, + Err(_) => Vec::with_capacity(0), + }; + + web::Json(bot_datas) +} + +#[derive(Serialize)] +struct ApiError { + error: String, + description: String, +} + +#[derive(Debug, Display)] +enum ApiErrorKind { + #[display(fmt = "Not Found")] + NotFound, +} + +impl ResponseError for ApiErrorKind { + fn error_response(&self) -> HttpResponse { + match *self { + ApiErrorKind::NotFound => HttpResponse::NotFound().json(ApiError { + error: self.to_string(), + description: String::from("The requested resource was not found"), + }), + } + } +} + +#[get("/bots/{name}")] +async fn get_bot(bot: web::Data<Addr<BotExecutor>>, name: web::Path<String>) -> impl Responder { + if let Some(bot_data) = bot.send(BotDataRequest(name.into_inner())).await.unwrap() { + Ok(web::Json(bot_data)) + } else { + Err(ApiErrorKind::NotFound) + } +} |
