feat: Some quality of life improvements
This commit is contained in:
@@ -25,6 +25,8 @@ pub struct MatrixConfig {
|
||||
pub struct OmegleConfig {
|
||||
#[serde(default)]
|
||||
pub websocket_url: String,
|
||||
#[serde(default)]
|
||||
pub selenium_url: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -33,11 +35,13 @@ impl Config {
|
||||
let username = env::var("MATRIX_USERNAME").unwrap_or_default();
|
||||
let password = env::var("MATRIX_PASSWORD").unwrap_or_default();
|
||||
let websocket_url = env::var("OMEGLE_WEBSOCKET_URL").unwrap_or_default();
|
||||
let selenium_url = env::var("SELENIUM_URL").unwrap_or_default();
|
||||
|
||||
if !homeserver.is_empty()
|
||||
|| !username.is_empty()
|
||||
|| !password.is_empty()
|
||||
|| !websocket_url.is_empty()
|
||||
|| !selenium_url.is_empty()
|
||||
{
|
||||
return Ok(Config {
|
||||
matrix: MatrixConfig {
|
||||
@@ -45,7 +49,10 @@ impl Config {
|
||||
username,
|
||||
password,
|
||||
},
|
||||
omegle: OmegleConfig { websocket_url },
|
||||
omegle: OmegleConfig {
|
||||
websocket_url,
|
||||
selenium_url,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ pub enum BotCommand {
|
||||
Connect { msg_id: OwnedEventId },
|
||||
Match { prefer_same_country: bool, user_id: String, msg_id: OwnedEventId },
|
||||
Skip { user_id: String, msg_id: OwnedEventId },
|
||||
Pause,
|
||||
Disconnect,
|
||||
Pause { reply_to: OwnedEventId },
|
||||
Disconnect { reply_to: OwnedEventId },
|
||||
SendMessage(String),
|
||||
SendTyping(bool),
|
||||
}
|
||||
@@ -78,6 +78,24 @@ async fn edit_status(room: &Room, event_id: OwnedEventId, text: &str, emoji: &st
|
||||
Ok(resp.event_id)
|
||||
}
|
||||
|
||||
async fn send_reply(room: &Room, event_id: OwnedEventId, text: &str) -> Result<OwnedEventId> {
|
||||
let mut content = RoomMessageEventContent::text_plain(text);
|
||||
content.relates_to = Some(Relation::Reply {
|
||||
in_reply_to: matrix_sdk::ruma::events::relation::InReplyTo::new(event_id),
|
||||
});
|
||||
let resp = room.send(content).await?;
|
||||
Ok(resp.event_id)
|
||||
}
|
||||
|
||||
async fn send_reply_html(room: &Room, event_id: OwnedEventId, text: &str, html: &str) -> Result<OwnedEventId> {
|
||||
let mut content = RoomMessageEventContent::text_html(text, html);
|
||||
content.relates_to = Some(Relation::Reply {
|
||||
in_reply_to: matrix_sdk::ruma::events::relation::InReplyTo::new(event_id),
|
||||
});
|
||||
let resp = room.send(content).await?;
|
||||
Ok(resp.event_id)
|
||||
}
|
||||
|
||||
|
||||
impl MatrixBot {
|
||||
pub async fn new(config: Config, db: Arc<Db>) -> Result<Self> {
|
||||
@@ -134,7 +152,7 @@ impl MatrixBot {
|
||||
if let MessageType::Text(text) = original.content.msgtype {
|
||||
let body = text.body.trim();
|
||||
if body.starts_with('!') {
|
||||
handle_command(body, &original.sender, &room, &db, &config, &handlers, &client).await;
|
||||
handle_command(body, &original.sender, &room, &db, &config, &handlers, &client, original.event_id).await;
|
||||
} else if let Some(tx) = handlers.get(&room_id) {
|
||||
let _ = tx.send(BotCommand::SendMessage(body.to_string())).await;
|
||||
}
|
||||
@@ -175,6 +193,7 @@ async fn handle_command(
|
||||
config: &Config,
|
||||
handlers: &Arc<DashMap<String, mpsc::Sender<BotCommand>>>,
|
||||
client: &Client,
|
||||
reply_to: OwnedEventId,
|
||||
) {
|
||||
let parts: Vec<&str> = body.split_whitespace().collect();
|
||||
let cmd = parts[0];
|
||||
@@ -185,24 +204,28 @@ async fn handle_command(
|
||||
"!help" => {
|
||||
let help_text = "<b>Available commands:</b><br/>\
|
||||
<code>!help</code> - Show this help message<br/>\
|
||||
<code>!connect</code> - Connect to Omgle WebSocket<br/>\
|
||||
<code>!connect</code> - Connect to Omegle WebSocket<br/>\
|
||||
<code>!match [--same-country]</code> - Request a new match (uses your interests)<br/>\
|
||||
<code>!skip</code> - Skip current peer and automatch (uses your interests)<br/>\
|
||||
<code>!stop</code> - Skip current peer without automatching<br/>\
|
||||
<code>!disconnect</code> - Disconnect from Omgle WebSocket<br/>\
|
||||
<code>!disconnect</code> - Disconnect from Omegle WebSocket<br/>\
|
||||
<code>!autoskip add/remove/list <CC></code> - Manage your automatic skipping list (global)<br/>\
|
||||
<code>!interests add/remove/list/clear <interest></code> - Manage your interests (global)";
|
||||
let plain = "Available commands:\n!help, !connect, !match, !skip, !stop, !pause, !disconnect, !autoskip, !interests";
|
||||
let content = RoomMessageEventContent::text_html(plain, help_text);
|
||||
let _ = room.send(content).await;
|
||||
let _ = send_reply_html(room, reply_to, plain, help_text).await;
|
||||
}
|
||||
"!connect" => {
|
||||
if handlers.contains_key(&room_id) {
|
||||
let _ = send_info(room, "Already connected").await;
|
||||
let _ = send_reply(room, reply_to, "Already connected").await;
|
||||
return;
|
||||
}
|
||||
|
||||
let msg_id = match send_info(room, "🔄 Connecting to Omgle...").await {
|
||||
let connecting_msg = if config.omegle.selenium_url.is_empty() {
|
||||
"🔄 Connecting to Omegle...".to_string()
|
||||
} else {
|
||||
format!("🔄 Connecting to Omegle... (If needed, complete bot check at {})", config.omegle.selenium_url)
|
||||
};
|
||||
let msg_id = match send_reply(room, reply_to, &connecting_msg).await {
|
||||
Ok(id) => id,
|
||||
Err(_) => return,
|
||||
};
|
||||
@@ -217,29 +240,29 @@ async fn handle_command(
|
||||
let client_clone = client.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut omgle_client = WsOmegleClient::new();
|
||||
if let Err(e) = omgle_client.connect(&config_clone.omegle.websocket_url).await {
|
||||
let mut omegle_client = WsOmegleClient::new();
|
||||
if let Err(e) = omegle_client.connect(&config_clone.omegle.websocket_url).await {
|
||||
let _ = edit_info(&room_clone, msg_id, &format!("❌ Failed to connect: {}", e)).await;
|
||||
handlers_clone.remove(&room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
let _ = tx.send(BotCommand::Connect { msg_id }).await;
|
||||
let _ = omgle_client.request_people_online().await;
|
||||
let _ = omegle_client.request_people_online().await;
|
||||
|
||||
handle_omgle_session(omgle_client, rx, room_clone, db_struct_clone, room_id.clone(), handlers_clone, client_clone).await;
|
||||
handle_omegle_session(omegle_client, rx, room_clone, db_struct_clone, room_id.clone(), handlers_clone, client_clone).await;
|
||||
});
|
||||
}
|
||||
"!match" => {
|
||||
if let Some(tx) = handlers.get(&room_id) {
|
||||
let prefer_same_country = parts.contains(&"--same-country");
|
||||
let msg_id = match send_info(room, "🔍 Matching...").await {
|
||||
let msg_id = match send_reply(room, reply_to, "🔍 Matching...").await {
|
||||
Ok(id) => id,
|
||||
Err(_) => return,
|
||||
};
|
||||
let _ = tx.send(BotCommand::Match { prefer_same_country, user_id, msg_id }).await;
|
||||
} else {
|
||||
let _ = send_info(room, "❌ Not connected to WebSocket. Use <code>!connect</code> first.").await;
|
||||
let _ = send_reply(room, reply_to, "❌ Not connected to WebSocket. Use <code>!connect</code> first.").await;
|
||||
}
|
||||
}
|
||||
"!interests" => {
|
||||
@@ -251,49 +274,49 @@ async fn handle_command(
|
||||
config.interests.push(i.to_string());
|
||||
}
|
||||
db.update_user_config(&config).unwrap();
|
||||
let _ = send_info(room, &format!("✅ Added interests: {:?}", &parts[2..])).await;
|
||||
let _ = send_reply(room, reply_to, &format!("✅ Added interests: {:?}", &parts[2..])).await;
|
||||
}
|
||||
"remove" => {
|
||||
config.interests.retain(|i| !parts[2..].contains(&i.as_str()));
|
||||
db.update_user_config(&config).unwrap();
|
||||
let _ = send_info(room, &format!("🗑️ Removed interests: {:?}", &parts[2..])).await;
|
||||
let _ = send_reply(room, reply_to, &format!("🗑️ Removed interests: {:?}", &parts[2..])).await;
|
||||
}
|
||||
"list" => {
|
||||
let _ = send_info(room, &format!("📝 Your interests: {:?}", config.interests)).await;
|
||||
let _ = send_reply(room, reply_to, &format!("📝 Your interests: {:?}", config.interests)).await;
|
||||
}
|
||||
"clear" => {
|
||||
config.interests.clear();
|
||||
db.update_user_config(&config).unwrap();
|
||||
let _ = send_info(room, "✨ Interests cleared").await;
|
||||
let _ = send_reply(room, reply_to, "✨ Interests cleared").await;
|
||||
}
|
||||
_ => {
|
||||
let _ = send_info(room, "❌ Invalid <code>!interests</code> subcommand.").await;
|
||||
let _ = send_reply(room, reply_to, "❌ Invalid <code>!interests</code> subcommand.").await;
|
||||
}
|
||||
}
|
||||
}
|
||||
"!skip" => {
|
||||
if let Some(tx) = handlers.get(&room_id) {
|
||||
let msg_id = match send_info(room, "⏩ Skipping...").await {
|
||||
let msg_id = match send_reply(room, reply_to, "⏩ Skipping...").await {
|
||||
Ok(id) => id,
|
||||
Err(_) => return,
|
||||
};
|
||||
let _ = tx.send(BotCommand::Skip { user_id, msg_id }).await;
|
||||
} else {
|
||||
let _ = send_info(room, "❌ Not connected to WebSocket.").await;
|
||||
let _ = send_reply(room, reply_to, "❌ Not connected to WebSocket.").await;
|
||||
}
|
||||
}
|
||||
"!stop" => {
|
||||
if let Some(tx) = handlers.get(&room_id) {
|
||||
let _ = tx.send(BotCommand::Pause).await;
|
||||
let _ = tx.send(BotCommand::Pause { reply_to }).await;
|
||||
} else {
|
||||
let _ = send_info(room, "❌ Not connected to WebSocket.").await;
|
||||
let _ = send_reply(room, reply_to, "❌ Not connected to WebSocket.").await;
|
||||
}
|
||||
}
|
||||
"!disconnect" => {
|
||||
if let Some(tx) = handlers.get(&room_id) {
|
||||
let _ = tx.send(BotCommand::Disconnect).await;
|
||||
let _ = tx.send(BotCommand::Disconnect { reply_to }).await;
|
||||
} else {
|
||||
let _ = send_info(room, "❌ Not connected to WebSocket.").await;
|
||||
let _ = send_reply(room, reply_to, "❌ Not connected to WebSocket.").await;
|
||||
}
|
||||
}
|
||||
"!autoskip" => {
|
||||
@@ -305,29 +328,29 @@ async fn handle_command(
|
||||
config.autoskip_countries.push(c.to_uppercase());
|
||||
}
|
||||
db.update_user_config(&config).unwrap();
|
||||
let _ = send_info(room, &format!("✅ Added to your skip list: {:?}", &parts[2..])).await;
|
||||
let _ = send_reply(room, reply_to, &format!("✅ Added to your skip list: {:?}", &parts[2..])).await;
|
||||
}
|
||||
"remove" => {
|
||||
config.autoskip_countries.retain(|c| !parts[2..].contains(&c.as_str()));
|
||||
db.update_user_config(&config).unwrap();
|
||||
let _ = send_info(room, &format!("🗑️ Removed from your skip list: {:?}", &parts[2..])).await;
|
||||
let _ = send_reply(room, reply_to, &format!("🗑️ Removed from your skip list: {:?}", &parts[2..])).await;
|
||||
}
|
||||
"list" => {
|
||||
let _ = send_info(room, &format!("📝 Your auto-skip countries: {:?}", config.autoskip_countries)).await;
|
||||
let _ = send_reply(room, reply_to, &format!("📝 Your auto-skip countries: {:?}", config.autoskip_countries)).await;
|
||||
}
|
||||
_ => {
|
||||
let _ = send_info(room, "❌ Invalid <code>!autoskip</code> subcommand.").await;
|
||||
let _ = send_reply(room, reply_to, "❌ Invalid <code>!autoskip</code> subcommand.").await;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let _ = send_info(room, &format!("❌ Invalid command: <code>{}</code>. Type <code>!help</code> for a list of commands.", cmd)).await;
|
||||
let _ = send_reply(room, reply_to, &format!("❌ Invalid command: <code>{}</code>. Type <code>!help</code> for a list of commands.", cmd)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_omgle_session(
|
||||
mut omgle: WsOmegleClient,
|
||||
async fn handle_omegle_session(
|
||||
mut omegle: WsOmegleClient,
|
||||
mut rx: mpsc::Receiver<BotCommand>,
|
||||
room: Room,
|
||||
db: Arc<Db>,
|
||||
@@ -350,17 +373,17 @@ async fn handle_omgle_session(
|
||||
cmd = rx.recv() => {
|
||||
match cmd {
|
||||
Some(BotCommand::Connect { msg_id }) => {
|
||||
let _ = edit_info(&room, msg_id, "✅ Connected to Omgle").await;
|
||||
let _ = edit_info(&room, msg_id, "✅ Connected to Omegle").await;
|
||||
},
|
||||
Some(BotCommand::Match { prefer_same_country, user_id, msg_id }) => {
|
||||
if peer_connected {
|
||||
let _ = omgle.disconnect_peer().await;
|
||||
let _ = omegle.disconnect_peer().await;
|
||||
}
|
||||
active_user_id = Some(user_id.clone());
|
||||
last_prefer_same_country = prefer_same_country;
|
||||
pending_msg_id = Some(msg_id);
|
||||
let user_config = db.get_user_config(&user_id).unwrap();
|
||||
let _ = omgle.request_match(prefer_same_country, user_config.interests).await;
|
||||
let _ = omegle.request_match(prefer_same_country, user_config.interests).await;
|
||||
|
||||
peer_connected = false;
|
||||
local_typing_active = false;
|
||||
@@ -377,14 +400,14 @@ async fn handle_omgle_session(
|
||||
active_user_id = Some(user_id.clone());
|
||||
pending_msg_id = Some(msg_id);
|
||||
let user_config = db.get_user_config(&user_id).unwrap();
|
||||
let _ = omgle.disconnect_peer().await;
|
||||
let _ = omegle.disconnect_peer().await;
|
||||
|
||||
peer_connected = false;
|
||||
local_typing_active = false;
|
||||
typing_active = false;
|
||||
let _ = room.typing_notice(false).await;
|
||||
|
||||
let _ = omgle.request_match(last_prefer_same_country, user_config.interests).await;
|
||||
let _ = omegle.request_match(last_prefer_same_country, user_config.interests).await;
|
||||
|
||||
let mut room_state = db.get_room_state(&room_id).unwrap();
|
||||
room_state.active_user_id = Some(user_id);
|
||||
@@ -393,44 +416,44 @@ async fn handle_omgle_session(
|
||||
let _ = edit_info(&room, msg_id, "❌ No stranger to skip.").await;
|
||||
}
|
||||
},
|
||||
Some(BotCommand::Pause) => {
|
||||
Some(BotCommand::Pause { reply_to }) => {
|
||||
if peer_connected {
|
||||
let _ = send_info(&room, "⏸️ Paused (Skipped peer)").await;
|
||||
let _ = omgle.disconnect_peer().await;
|
||||
let _ = send_reply(&room, reply_to, "⏸️ Paused (Skipped peer)").await;
|
||||
let _ = omegle.disconnect_peer().await;
|
||||
peer_connected = false;
|
||||
local_typing_active = false;
|
||||
typing_active = false;
|
||||
let _ = room.typing_notice(false).await;
|
||||
} else {
|
||||
let _ = send_info(&room, "❌ No stranger to pause.").await;
|
||||
let _ = send_reply(&room, reply_to, "❌ No stranger to pause.").await;
|
||||
}
|
||||
},
|
||||
Some(BotCommand::Disconnect) => {
|
||||
let _ = omgle.disconnect().await;
|
||||
Some(BotCommand::Disconnect { reply_to }) => {
|
||||
let _ = omegle.disconnect().await;
|
||||
let mut room_state = db.get_room_state(&room_id).unwrap();
|
||||
room_state.is_connected = false;
|
||||
let _ = db.update_room_state(&room_state);
|
||||
let _ = send_info(&room, "🔌 Disconnected from Omgle").await;
|
||||
let _ = send_reply(&room, reply_to, "🔌 Disconnected from Omegle").await;
|
||||
handlers.remove(&room_id);
|
||||
return;
|
||||
},
|
||||
Some(BotCommand::SendMessage(text)) => {
|
||||
if peer_connected {
|
||||
local_typing_active = false;
|
||||
let _ = omgle.send_message(&text).await;
|
||||
let _ = omegle.send_message(&text).await;
|
||||
message_count += 1;
|
||||
}
|
||||
},
|
||||
Some(BotCommand::SendTyping(typing)) => {
|
||||
if peer_connected {
|
||||
local_typing_active = typing;
|
||||
let _ = omgle.send_typing(typing).await;
|
||||
let _ = omegle.send_typing(typing).await;
|
||||
}
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
ev = omgle.next_event() => {
|
||||
ev = omegle.next_event() => {
|
||||
match ev {
|
||||
Ok(Some(msg)) => {
|
||||
match msg.channel.as_str() {
|
||||
@@ -454,14 +477,14 @@ async fn handle_omgle_session(
|
||||
let config = db.get_user_config(user_id).unwrap();
|
||||
if config.autoskip_countries.contains(&data.country.to_uppercase()) {
|
||||
pending_msg_id = send_info(&room, "⏩ Auto-skipping...").await.ok();
|
||||
let _ = omgle.disconnect_peer().await;
|
||||
let _ = omegle.disconnect_peer().await;
|
||||
|
||||
peer_connected = false;
|
||||
local_typing_active = false;
|
||||
typing_active = false;
|
||||
let _ = room.typing_notice(false).await;
|
||||
|
||||
let _ = omgle.request_match(last_prefer_same_country, config.interests).await;
|
||||
let _ = omegle.request_match(last_prefer_same_country, config.interests).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,7 +519,7 @@ async fn handle_omgle_session(
|
||||
if let Some(user_id) = &active_user_id {
|
||||
pending_msg_id = send_info(&room, "⏩ Automatching...").await.ok();
|
||||
let config = db.get_user_config(user_id).unwrap();
|
||||
let _ = omgle.request_match(last_prefer_same_country, config.interests).await;
|
||||
let _ = omegle.request_match(last_prefer_same_country, config.interests).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,11 +538,11 @@ async fn handle_omgle_session(
|
||||
}
|
||||
|
||||
if local_typing_active && peer_connected {
|
||||
let _ = omgle.send_typing(true).await;
|
||||
let _ = omegle.send_typing(true).await;
|
||||
}
|
||||
|
||||
if last_people_online_request.elapsed().as_secs() >= 60 {
|
||||
let _ = omgle.request_people_online().await;
|
||||
let _ = omegle.request_people_online().await;
|
||||
last_people_online_request = std::time::Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,18 @@ impl WsOmegleClient {
|
||||
impl OmegleProvider for WsOmegleClient {
|
||||
async fn connect(&mut self, url: &str) -> Result<()> {
|
||||
let (ws_stream, _) = connect_async(url).await?;
|
||||
self.ws = Some(ws_stream);
|
||||
|
||||
let mut ws = ws_stream;
|
||||
let msg = ws.next().await.ok_or_else(|| anyhow!("Connection closed before receiving SUCCESS"))??;
|
||||
|
||||
match msg {
|
||||
Message::Text(text) if text == "SUCCESS" => {}
|
||||
Message::Text(text) => return Err(anyhow!("Unexpected message: {}", text)),
|
||||
Message::Close(_) => return Err(anyhow!("Server closed connection")),
|
||||
_ => return Err(anyhow!("Unexpected message type")),
|
||||
}
|
||||
|
||||
self.ws = Some(ws);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user