tenebrous-dicebot/dicebot/src/rpc.rs

149 lines
4.2 KiB
Rust

use crate::db::{errors::DataError, Variables};
use crate::error::BotError;
use crate::matrix;
use crate::{config::Config, db::sqlite::Database};
use futures::stream;
use futures::{StreamExt, TryFutureExt, TryStreamExt};
use log::info;
use matrix_sdk::{identifiers::UserId, Client};
use std::convert::TryFrom;
use std::sync::Arc;
use tenebrous_rpc::protos::dicebot::{
dicebot_server::{Dicebot, DicebotServer},
rooms_list_reply::Room,
GetAllVariablesReply, GetAllVariablesRequest, RoomsListReply, SetVariableReply,
SetVariableRequest, UserIdRequest,
};
use tenebrous_rpc::protos::dicebot::{GetVariableReply, GetVariableRequest};
use tonic::{transport::Server, Code, Request, Response, Status};
impl From<BotError> for Status {
fn from(error: BotError) -> Status {
Status::new(Code::Internal, error.to_string())
}
}
impl From<DataError> for Status {
fn from(error: DataError) -> Status {
Status::new(Code::Internal, error.to_string())
}
}
pub struct DicebotRpcService {
config: Arc<Config>,
db: Database,
client: Client,
}
#[tonic::async_trait]
impl Dicebot for DicebotRpcService {
async fn set_variable(
&self,
request: Request<SetVariableRequest>,
) -> Result<Response<SetVariableReply>, Status> {
let SetVariableRequest {
user_id,
room_id,
variable_name,
value,
} = request.into_inner();
self.db
.set_user_variable(&user_id, &room_id, &variable_name, value)
.await?;
Ok(Response::new(SetVariableReply { success: true }))
}
async fn get_variable(
&self,
request: Request<GetVariableRequest>,
) -> Result<Response<GetVariableReply>, Status> {
let request = request.into_inner();
let value = self
.db
.get_user_variable(&request.user_id, &request.room_id, &request.variable_name)
.await?;
Ok(Response::new(GetVariableReply { value }))
}
async fn get_all_variables(
&self,
request: Request<GetAllVariablesRequest>,
) -> Result<Response<GetAllVariablesReply>, Status> {
let request = request.into_inner();
let variables = self
.db
.get_user_variables(&request.user_id, &request.room_id)
.await?;
Ok(Response::new(GetAllVariablesReply { variables }))
}
async fn rooms_for_user(
&self,
request: Request<UserIdRequest>,
) -> Result<Response<RoomsListReply>, Status> {
let UserIdRequest { user_id } = request.into_inner();
let user_id = UserId::try_from(user_id).map_err(BotError::from)?;
let rooms_for_user = matrix::get_rooms_for_user(&self.client, &user_id)
.err_into::<BotError>()
.await?;
let mut rooms: Vec<Room> = stream::iter(rooms_for_user)
.filter_map(|room| async move {
let rooms = room.display_name().await.map(|room_name| Room {
room_id: room.room_id().to_string(),
display_name: room_name,
});
Some(rooms)
})
.err_into::<BotError>()
.try_collect()
.await?;
let sort = |r1: &Room, r2: &Room| {
r1.display_name
.to_lowercase()
.cmp(&r2.display_name.to_lowercase())
};
rooms.sort_by(sort);
Ok(Response::new(RoomsListReply { rooms }))
}
}
pub async fn serve_grpc(
config: &Arc<Config>,
db: &Database,
client: &Client,
) -> Result<(), BotError> {
match config.rpc_addr() {
Some(addr) => {
let addr = addr.parse()?;
let rpc_service = DicebotRpcService {
db: db.clone(),
config: config.clone(),
client: client.clone(),
};
info!("Serving Dicebot gRPC service on {}", addr);
Server::builder()
.add_service(DicebotServer::new(rpc_service))
.serve(addr)
.await
.map_err(|e| e.into())
}
_ => noop().await,
}
}
pub async fn noop() -> Result<(), BotError> {
info!("RPC address not specified. Not enabling gRPC.");
Ok(())
}