Initial commit
commit
d975537ddd
@ -0,0 +1,2 @@
|
||||
target/
|
||||
Cargo.lock
|
@ -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"
|
@ -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 <http://unlicense.org/>
|
@ -0,0 +1,3 @@
|
||||
# log-viewer
|
||||
This is a log viewer for discord_ai_bot.
|
||||
Yes I am insane.
|
@ -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<Message>,
|
||||
history: Vec<Log>,
|
||||
autoscroll: bool,
|
||||
description: String,
|
||||
selected_channel: String,
|
||||
channels: Vec<String>,
|
||||
description_shown: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Log {
|
||||
name: String,
|
||||
description: String,
|
||||
message: String,
|
||||
channel: String,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(rx: Receiver<Message>) -> 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();
|
||||
}
|
||||
}
|
@ -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!");
|
||||
}
|
||||
}
|
Loading…
Reference in new issue