aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJokler <jokler@protonmail.com>2020-02-03 01:14:05 +0100
committerJokler <jokler@protonmail.com>2020-02-22 23:20:10 +0100
commit5eea11a03c11551091b2c72f48590aec7f5410f0 (patch)
tree4767841f341b82539c2586ef04e67fe72e52bd4b /src
parent2831c2b60cb61a14c7efee4ab5c0389eb3ad5469 (diff)
downloadpokebot-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.rs14
-rw-r--r--src/bot/music.rs3
-rw-r--r--src/web_server.rs90
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)
+ }
+}