commit d975537dddefd88122ca8d54b1ba749842303e04 Author: Tymon Date: Tue Nov 8 19:44:06 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4470988 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target/ +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5119534 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "log-viewer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +lto = "fat" +opt-level = "s" +panic = "abort" + +[dependencies] +eframe = "0.19.0" +serde = { version = "1.0.145", features = ["derive"] } +serde_json = "1.0.86" +tungstenite = "0.17.3" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b0a72fb --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..40f84cc --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# log-viewer +This is a log viewer for discord_ai_bot. +Yes I am insane. \ No newline at end of file diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..2dab600 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,121 @@ +use std::sync::mpsc::Receiver; + +use eframe::{ + egui::{self, RichText, ScrollArea, Window}, + emath::Align, + epaint::Color32, +}; +use serde::{Deserialize, Serialize}; +use tungstenite::Message; + +pub struct App { + rx: Receiver, + history: Vec, + autoscroll: bool, + description: String, + selected_channel: String, + channels: Vec, + description_shown: bool, +} + +#[derive(Serialize, Deserialize)] +struct Log { + name: String, + description: String, + message: String, + channel: String, +} + +impl App { + pub fn new(rx: Receiver) -> Self { + Self { + rx, + history: Vec::new(), + autoscroll: false, + description: "".to_string(), + selected_channel: "".to_string(), + channels: Vec::new(), + description_shown: false, + } + } +} + +impl eframe::App for App { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { + let message = self.rx.try_recv(); + // TODO: Add toasts for things like debug information and status changes (https://github.com/urholaukkarinen/egui-toast) + if let Ok(message) = message { + let text = message.to_string(); + + let json = serde_json::from_str(&text); + + if let Ok(json) = json { + self.history.push(json); + } + } + egui::CentralPanel::default().show(ctx, |ui| { + Window::new("Description") + .open(&mut self.description_shown) + .collapsible(false) + .show(ctx, |ui| { + ScrollArea::vertical() + .id_source("description_box") + .show(ui, |ui| { + ui.label(&self.description); + }); + }); + ui.horizontal(|ui| { + ui.checkbox(&mut self.autoscroll, "Autoscroll"); + if ui.button("Reset").clicked() { + self.history.clear(); + self.channels.clear(); + self.selected_channel.clear(); + self.description.clear(); + } + if ui.button("Description").clicked() { + self.description_shown = true; + } + }); + ui.separator(); + ui.horizontal(|ui| { + ScrollArea::horizontal() + .id_source("channel_bar") + .show(ui, |ui| { + for channel in &self.channels { + if ui.button(channel).clicked() { + self.selected_channel = channel.to_owned(); + } + } + }); + }); + ui.separator(); + ScrollArea::vertical() + .id_source("messages_list") + .show(ui, |ui| { + for msg in &self.history { + if !self.channels.contains(&msg.channel) { + self.channels.push((&msg.channel).to_owned()); + } + + if msg.channel == self.selected_channel { + ui.horizontal(|ui| { + ui.label(RichText::new(&msg.name).color(Color32::LIGHT_GRAY)); + if !&msg.description.is_empty() { + self.description = (&msg.description).to_owned(); + ui.label(RichText::new("[AI]").color(Color32::DARK_GRAY)); + } + }); + ui.label(&msg.message); + ui.separator(); + } + + if self.autoscroll { + ui.scroll_to_cursor(Some(Align::BOTTOM)); + } + } + }); + }); + + ctx.request_repaint(); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..51ae21a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,47 @@ +use std::{sync::mpsc::channel, thread::spawn}; + +use app::App; +use eframe::egui::{self}; +use tungstenite::{connect, Message}; + +mod app; + +fn main() { + let address = std::env::args().nth(1).expect("No url given!"); + + if let Ok(ws) = connect(address) { + let (mut socket, _) = ws; + let (tx, rx) = channel(); + + spawn(move || loop { + let message = socket.read_message(); + + if let Ok(message) = message { + if message.to_string() == "__PING__" { + socket + .write_message(Message::Text("__PONG__".to_string())) + .expect("Failed to respond to ping! Did the WebSocket server die?"); + } + + tx.send(message) + .expect("Failed to send message to channel! (This shouldn't be possible)"); + } + }); + + eframe::run_native( + "Log Viewer", + eframe::NativeOptions::default(), + Box::new(|cc| { + let mut fonts = egui::FontDefinitions::default(); + fonts.font_data.iter_mut().for_each(|font| { + font.1.tweak.scale = 2.0; + }); + cc.egui_ctx.set_fonts(fonts); + + Box::new(App::new(rx)) + }), + ); + } else { + panic!("Could not connect to websocket!"); + } +}