shared_child = "1"
rsevents = "0.3"
-
[profile.release]
codegen-units = 1
lto = true
+strip = true
initdata.properties = Ice.createProperties(None, initdata.properties)
initdata.properties.setProperty('Ice.ImplicitContext', 'Shared')
+# Return a tuple (path: string, positions: int list)
+# where 'positions' is the position of each path level.
def getChannelPath(channels, channelId, first=True):
if channelId == 0:
- return "/"
+ return ("/", [0])
else:
channel = channels[channelId]
- return getChannelPath(channels, channel.parent, False) + channel.name + \
- ("" if first else "/")
+ (path, positions) = getChannelPath(channels, channel.parent, False)
+ return (path + channel.name + ("" if first else "/"), positions + [channel.position])
def getUsers(server):
return map(lambda u: (u.name, u.channel, u.selfMute, u.selfDeaf), server.getUsers().values())
+def userToJson(channels, user):
+ (username, channelId, selfMute, selfDeaf) = user
+ (channelPath, positions) = getChannelPath(channels, channelId)
+ return {
+ 'username': username,
+ 'channelPath': channelPath,
+ 'channelPathPositions': positions,
+ 'selfMute': selfMute,
+ 'selfDeaf': selfDeaf
+ }
+
def getUsersAsJson(server):
channels = server.getChannels()
users = getUsers(server)
+
return json.dumps(
- [
- {
- 'username': username,
- 'channel': getChannelPath(channels, channel),
- 'selfMute': selfMute,
- 'selfDeaf': selfDeaf
- }
- for (username, channel, selfMute, selfDeaf) in users
- ]
+ list(map(lambda user: userToJson(channels, user), users))
)
with Ice.initialize(sys.argv, initdata) as commmunicator:
meta = MumbleServer.MetaPrx.uncheckedCast(proxy)
server = meta.getServer(1)
+ # print(getUsersAsJson(server))
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 4978))
s.listen()
while True:
time::{self, Duration},
};
-use json;
+use itertools::Itertools;
+use json::{self, JsonValue};
use rsevents::{AutoResetEvent, Awaitable, EventState};
use shared_child::SharedChild;
#[derive(Debug, Clone)]
pub struct User {
pub name: String,
- pub channel: String,
+ pub channel_path: String,
+ pub channel_path_positions: Vec<i32>,
pub self_mute: bool,
- pub self_deaf: bool, // 🔇
+ pub self_deaf: bool,
}
pub struct Ice {
let mut command = Command::new(PYTHON_PATH);
command.arg("./ice/ice.py");
- let child = SharedChild::spawn(&mut command).unwrap();
- let child_arc = Arc::new(child);
- let child_arc_cloned = child_arc.clone();
-
- thread::spawn(move || {
- kill_event_arc_clone1.wait();
- child_arc_cloned.kill()
- });
-
- println!("ice.py launched!");
-
- child_arc.wait().unwrap();
-
- // if kill_event is set then quit loop.
- if dropped_arc_clone.load(Ordering::Relaxed) {
- println!("Stopping main python loop...");
- kill_event_arc_clone2.set();
- return;
- } else {
- kill_event_arc_clone2.set();
+ match SharedChild::spawn(&mut command) {
+ Ok(child) => {
+ let child_arc = Arc::new(child);
+ let child_arc_cloned = child_arc.clone();
+
+ thread::spawn(move || {
+ kill_event_arc_clone1.wait();
+ child_arc_cloned.kill()
+ });
+
+ println!("ice.py launched!");
+
+ if let Err(err) = child_arc.wait() {
+ println!("Error when waiting python process: {}", err);
+ }
+
+ // if kill_event is set then quit loop.
+ if dropped_arc_clone.load(Ordering::Relaxed) {
+ println!("Stopping main python loop...");
+ kill_event_arc_clone2.set();
+ return;
+ } else {
+ kill_event_arc_clone2.set();
+ }
+
+ print!("Python stopped!");
+ }
+ Err(err) => print!("Can't lauch python: {}, retrying in a while...", err),
}
- print!("Python stopped!");
-
let elapsed = time::Instant::now() - time_beginning_loop;
if elapsed < RELAUNCH_PERIOD {
let to_wait = RELAUNCH_PERIOD - elapsed;
let mut users: Vec<User> = vec![];
for i in 0..json.len() {
let user_json = &json[i];
+
users.push(User {
name: user_json["username"].as_str().unwrap().to_string(),
- channel: user_json["channel"].as_str().unwrap().to_string(),
+ channel_path: user_json["channelPath"].as_str().unwrap().to_string(),
+ // TODO: a bit ugly.
+ channel_path_positions: match &user_json["channelPathPositions"] {
+ JsonValue::Array(array) => {
+ array.iter().map(|v| v.as_i32().unwrap()).collect_vec()
+ }
+ _ => vec![],
+ },
self_mute: user_json["selfMute"].as_bool().unwrap(),
self_deaf: user_json["selfDeaf"].as_bool().unwrap(),
});
}
+
+ // dbg!(&users);
+
self.cached_users = users.clone();
self.cached_users_timestamp = time::Instant::now();
users
use std::{
- cmp::Ordering,
+ cmp::{min, Ordering},
net::SocketAddr,
sync::{Arc, Mutex},
};
use askama::Template;
use askama_axum::IntoResponse;
use axum::{extract::State, routing::get, Router};
+use itertools::Itertools;
use tokio::signal;
use tower_http::services::ServeDir;
mod ice;
-#[derive(Eq)]
+#[derive(Eq, Debug)]
struct User {
name: String,
self_mute: bool,
}
}
+#[derive(Eq, Debug)]
struct Channel {
- name: String,
+ path: String,
+ positions: Vec<i32>,
users: Vec<User>,
}
+impl PartialEq for Channel {
+ fn eq(&self, other: &Self) -> bool {
+ self.path.eq(&other.path)
+ }
+}
+
+impl PartialOrd for Channel {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(&other))
+ }
+}
+
+impl Ord for Channel {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let self_path = self.path.split('/').collect_vec();
+ let other_path = other.path.split('/').collect_vec();
+ let l = min(self.positions.len(), other.positions.len());
+ for i in 0..l {
+ if let order @ (Ordering::Greater | Ordering::Less) =
+ self.positions[i].cmp(&other.positions[i])
+ {
+ return order;
+ }
+
+ if let order @ (Ordering::Greater | Ordering::Less) = self_path[i].cmp(other_path[i]) {
+ return order;
+ }
+
+ if i == l - 1 {
+ return self.positions.len().cmp(&other.positions.len());
+ }
+ }
+ Ordering::Equal
+ }
+}
+
#[derive(Template)]
#[template(path = "main.html")]
struct MainTemplate {
let mut channels = Vec::<Channel>::new();
'next_user: for u in &users {
for c in &mut channels {
- if c.name == u.channel {
+ if c.path == u.channel_path {
c.users.push(User::from_ice_user(u));
continue 'next_user;
}
}
let channel = Channel {
- name: u.channel.clone(),
+ path: u.channel_path.clone(),
+ positions: u.channel_path_positions.clone(),
users: vec![User::from_ice_user(u)],
};
channels.push(channel);
c.users.sort();
}
+ channels.sort();
+
MainTemplate { channels }
}
<p>No user connected</p>
{% else %}
{% for channel in channels %}
- <h2 class="channel">{{ channel.name }}</h2>
+ <h2 class="channel">{{ channel.path }}</h2>
<ul>
{% for user in channel.users %}
<li>{{ user.name }}