feat: more robust logic and handle discord reconnects
This commit is contained in:
parent
43dab27968
commit
30034b9f60
2 changed files with 78 additions and 38 deletions
|
@ -1,34 +1,32 @@
|
||||||
/*
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
* This file WAS ONCE is part of discord-presence. Extension for Zed that
|
|
||||||
* adds support for Discord Rich Presence using LSP. It is heavily modified
|
|
||||||
* to be used here.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 Steinhübl, Virt
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use discord_rich_presence::{
|
use discord_rich_presence::{
|
||||||
DiscordIpc, DiscordIpcClient,
|
DiscordIpc, DiscordIpcClient,
|
||||||
activity::{Activity, Assets, Button, Timestamps},
|
activity::{Activity, Assets, Button, Timestamps},
|
||||||
};
|
};
|
||||||
use log::{debug, error, info};
|
use log::{debug, info};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Discord {
|
pub struct Discord {
|
||||||
client: DiscordIpcClient,
|
client: DiscordIpcClient,
|
||||||
|
connected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Discord {
|
impl Discord {
|
||||||
pub fn new(id: &str) -> Result<Self> {
|
pub fn new(id: &str) -> Result<Self> {
|
||||||
Ok(Self { client: DiscordIpcClient::new(id) })
|
Ok(Self { client: DiscordIpcClient::new(id), connected: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_connected(&self) -> bool {
|
||||||
|
self.connected
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn connect(&mut self) -> Result<()> {
|
pub fn connect(&mut self) -> Result<()> {
|
||||||
debug!("connecting to discord");
|
debug!("connecting to discord");
|
||||||
|
|
||||||
self.client.connect().context("failed to connect to discord ipc")?;
|
self.client.connect().context("failed to connect to discord ipc")?;
|
||||||
|
self.connected = true;
|
||||||
|
|
||||||
info!("successfully connected to discord");
|
info!("successfully connected to discord");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -37,7 +35,10 @@ impl Discord {
|
||||||
pub fn disconnect(&mut self) -> Result<()> {
|
pub fn disconnect(&mut self) -> Result<()> {
|
||||||
debug!("disconnecting from discord");
|
debug!("disconnecting from discord");
|
||||||
|
|
||||||
self.client.close().context("failed to disconnect from discord")
|
self.client.close().context("failed to disconnect from discord")?;
|
||||||
|
self.connected = false;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) -> Result<()> {
|
pub fn clear(&mut self) -> Result<()> {
|
||||||
|
|
91
src/main.rs
91
src/main.rs
|
@ -1,6 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
thread::sleep,
|
|
||||||
time::{Duration, Instant, SystemTime},
|
time::{Duration, Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,7 +7,7 @@ use anyhow::Result;
|
||||||
use discord::Discord;
|
use discord::Discord;
|
||||||
use hyprland::{Address, Hyprland};
|
use hyprland::{Address, Hyprland};
|
||||||
use language::get_language;
|
use language::get_language;
|
||||||
use log::{debug, info};
|
use log::{debug, error, info, warn};
|
||||||
|
|
||||||
pub mod discord;
|
pub mod discord;
|
||||||
pub mod hyprland;
|
pub mod hyprland;
|
||||||
|
@ -19,7 +18,7 @@ const ICONS_URL: &str =
|
||||||
"https://raw.githubusercontent.com/xhyrom/zed-discord-presence/main/assets/icons";
|
"https://raw.githubusercontent.com/xhyrom/zed-discord-presence/main/assets/icons";
|
||||||
const ZED_CLASS: &str = "dev.zed.Zed";
|
const ZED_CLASS: &str = "dev.zed.Zed";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
struct ZedInstance {
|
struct ZedInstance {
|
||||||
workspace: String,
|
workspace: String,
|
||||||
file: Option<String>,
|
file: Option<String>,
|
||||||
|
@ -29,6 +28,14 @@ struct ZedInstance {
|
||||||
focused: SystemTime,
|
focused: SystemTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PartialEq for ZedInstance {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.workspace.eq(&other.workspace)
|
||||||
|
&& self.file.eq(&other.file)
|
||||||
|
&& self.started.eq(&other.started)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ZedInstance {
|
impl ZedInstance {
|
||||||
pub fn new(title: &str) -> Self {
|
pub fn new(title: &str) -> Self {
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
|
@ -79,19 +86,18 @@ fn main() -> Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("connecting to discord ipc");
|
|
||||||
discord.connect()?;
|
|
||||||
info!("connecting to hyprland ipc");
|
info!("connecting to hyprland ipc");
|
||||||
hyprland.connect(Duration::from_secs(60))?;
|
hyprland.connect(Duration::from_secs(60))?;
|
||||||
|
|
||||||
let mut first = true;
|
let mut updated = Instant::now() - idle;
|
||||||
|
let mut shown: Option<ZedInstance> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let events = hyprland.read_events()?;
|
let events = hyprland.read_events()?;
|
||||||
debug!("received new hyprland events");
|
debug!("received new hyprland events");
|
||||||
|
|
||||||
// only check on timeout if not active
|
// update anyways if last update has been before idle timeout
|
||||||
let mut changed = first || events.is_none() && !instances.contains_key(&active);
|
let mut changed = Instant::now().duration_since(updated) < idle;
|
||||||
first = false;
|
|
||||||
|
|
||||||
if let Some(events) = events {
|
if let Some(events) = events {
|
||||||
for event in events {
|
for event in events {
|
||||||
|
@ -131,27 +137,55 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
if let Some(instance) = instances.get(&active).or_else(|| {
|
debug!("checking for instance change");
|
||||||
|
|
||||||
|
let instance = instances.get(&active).or_else(|| {
|
||||||
instances.values().max_by(|a, b| a.focused.cmp(&b.focused)).filter(|instance| {
|
instances.values().max_by(|a, b| a.focused.cmp(&b.focused)).filter(|instance| {
|
||||||
SystemTime::now().duration_since(instance.focused).expect("compare times wtf")
|
SystemTime::now().duration_since(instance.focused).expect("compare times wtf")
|
||||||
< idle
|
< idle
|
||||||
})
|
})
|
||||||
}) {
|
});
|
||||||
info!(
|
|
||||||
"setting discord activity to {} in workspace {}",
|
|
||||||
instance.file.as_ref().map(|s| s.as_str()).unwrap_or("<none>"),
|
|
||||||
instance.workspace
|
|
||||||
);
|
|
||||||
|
|
||||||
set_activity(
|
if instance != shown.as_ref() {
|
||||||
&mut discord,
|
debug!("updating discord status as change was detected");
|
||||||
instance.file.as_ref().map(|s| s.as_str()).unwrap_or("nothing"),
|
shown = instance.cloned();
|
||||||
&instance.workspace,
|
updated = Instant::now();
|
||||||
instance.started,
|
|
||||||
)?
|
if !discord.is_connected() {
|
||||||
} else {
|
info!("(re-)connecting to discord ipc");
|
||||||
info!("removing discord activity");
|
|
||||||
discord.clear()?;
|
if let Err(e) = discord.connect() {
|
||||||
|
warn!("failed to connect to discord, waiting for next update: {e:#}");
|
||||||
|
shown = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = if let Some(ref instance) = shown {
|
||||||
|
info!(
|
||||||
|
"setting discord activity to {} in workspace {}",
|
||||||
|
instance.file.as_ref().map(|s| s.as_str()).unwrap_or("<none>"),
|
||||||
|
instance.workspace
|
||||||
|
);
|
||||||
|
set_activity(
|
||||||
|
&mut discord,
|
||||||
|
instance.file.as_ref().map(|s| s.as_str()).unwrap_or("nothing"),
|
||||||
|
&instance.workspace,
|
||||||
|
instance.started,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
info!("removing discord activity");
|
||||||
|
discord.clear()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = result {
|
||||||
|
warn!("failed to set discord activity: {e:#}");
|
||||||
|
|
||||||
|
debug!("disconnecting from discord");
|
||||||
|
discord.disconnect().map_err(|e| error!("failed to disconnect: {e:#}")).ok();
|
||||||
|
|
||||||
|
shown = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,7 +198,12 @@ pub fn set_activity(
|
||||||
started: SystemTime,
|
started: SystemTime,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let language = get_language(file);
|
let language = get_language(file);
|
||||||
let language_upper: String = language.chars().take(1).chain(language.chars().skip(1)).collect();
|
let language_upper: String = language
|
||||||
|
.chars()
|
||||||
|
.take(1)
|
||||||
|
.map(|a| a.to_ascii_uppercase())
|
||||||
|
.chain(language.chars().skip(1))
|
||||||
|
.collect();
|
||||||
|
|
||||||
discord.set(
|
discord.set(
|
||||||
Some(format!("Editing {file}")),
|
Some(format!("Editing {file}")),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue