use anyhow::Result;
use vrchatapi::apis;
use vrchatapi::models::InviteRequest;
pub async fn handle_invite_send_action(
api_config: &vrchatapi::apis::configuration::Configuration,
user: &str,
instance_id: Option<String>,
use_direct_id: bool,
request_invite: bool,
message_slot: Option<i32>,
) -> Result<()> {
let user_id =
crate::common::user_operations::resolve_user_identifier(api_config, user, use_direct_id)
.await?;
let slot = message_slot.unwrap_or(0);
match (instance_id, request_invite) {
(Some(instance), false) => {
send_invite_to_instance(api_config, &user_id, &instance, slot).await
}
(None, true) => {
request_invite_from_user(api_config, &user_id, slot).await
}
(None, false) => {
Err(anyhow::anyhow!(
"Must specify either an instance_id or use --request flag to request an invite"
))
}
(Some(_), true) => {
Err(anyhow::anyhow!(
"Cannot specify both instance_id and --request flag"
))
}
}
}
async fn send_invite_to_instance(
api_config: &vrchatapi::apis::configuration::Configuration,
user_id: &str,
instance_id: &str,
message_slot: i32,
) -> Result<()> {
let invite_request = InviteRequest {
instance_id: instance_id.to_string(),
message_slot: Some(message_slot),
};
match apis::invite_api::invite_user(api_config, user_id, invite_request).await {
Ok(notification) => {
println!("Invite sent successfully!");
println!("Notification ID: {}", notification.id);
println!("Target Instance: {instance_id}");
if !notification.message.is_empty() {
println!("Message: {}", notification.message);
}
if let Some(invite_details) = notification.details.as_object() {
if let Some(instance_id_detail) =
invite_details.get("instanceId").and_then(|v| v.as_str())
{
println!("Invite Instance ID: {instance_id_detail}");
}
}
Ok(())
}
Err(e) => {
eprintln!("Failed to send invite: {e}");
Err(anyhow::anyhow!("Failed to send invite: {}", e))
}
}
}
async fn request_invite_from_user(
api_config: &vrchatapi::apis::configuration::Configuration,
user_id: &str,
_message_slot: i32,
) -> Result<()> {
let request_payload = serde_json::json!({});
let base_url = &api_config.base_path;
let url = format!("{base_url}/requestInvite/{user_id}");
let mut request_builder = api_config.client.post(&url);
if let Some(user_agent) = &api_config.user_agent {
request_builder = request_builder.header("User-Agent", user_agent);
}
request_builder = request_builder.json(&request_payload);
let response = request_builder.send().await?;
let status = response.status();
let response_text = response.text().await?;
if status.is_success() {
match serde_json::from_str::<serde_json::Value>(&response_text) {
Ok(_) => {
println!("✅ Successfully requested invite using traditional method!");
}
Err(e) => {
eprintln!("Failed to parse response JSON: {e}");
eprintln!("Raw response: {response_text}");
return Err(anyhow::anyhow!("Failed to parse response: {}", e));
}
}
} else {
eprintln!("Request failed with status: {status}");
eprintln!("Response body: {response_text}");
return Err(anyhow::anyhow!("Request failed with status: {}", status));
}
Ok(())
}
pub async fn handle_invite_request_action(
api_config: &vrchatapi::apis::configuration::Configuration,
user: &str,
use_direct_id: bool,
message_slot: Option<i32>,
force_request: bool,
) -> Result<()> {
let user_id =
crate::common::user_operations::resolve_user_identifier(api_config, user, use_direct_id)
.await?;
let slot = message_slot.unwrap_or(0);
if force_request {
println!("🔄 Using traditional invite request (--force-request specified)");
return request_invite_from_user(api_config, &user_id, slot).await;
}
match crate::common::user_operations::fetch_user_by_resolved_id(api_config, &user_id).await {
Ok(user_info) => {
if user_info.status != vrchatapi::models::UserStatus::Offline {
if let Some(location) = &user_info.location {
if !location.is_empty() && location != "private" && location.contains(':') {
let parts: Vec<&str> = location.split(':').collect();
if parts.len() >= 2 {
let world_id = parts[0];
let full_instance_part = parts[1];
let instance_id = if let Some(tilde_pos) = full_instance_part.find('~')
{
&full_instance_part[..tilde_pos]
} else {
full_instance_part
};
println!(
"🎯 Detected user location, attempting to use automatic invite..."
);
match invite_myself_to_instance(api_config, world_id, instance_id).await
{
Ok(_) => {
return Ok(());
}
Err(_) => {
println!("⚠️ First attempt failed, trying with full instance identifier...");
match invite_myself_to_instance(
api_config,
world_id,
full_instance_part,
)
.await
{
Ok(_) => {
return Ok(());
}
Err(_) => {
println!("⚠️ Automatic invite failed, falling back to traditional invite request...");
}
}
}
}
} else {
}
}
}
}
}
Err(_) => {
}
}
println!("📞 Using traditional invite request method...");
request_invite_from_user(api_config, &user_id, slot).await
}
async fn invite_myself_to_instance(
api_config: &vrchatapi::apis::configuration::Configuration,
world_id: &str,
instance_id: &str,
) -> Result<()> {
match apis::invite_api::invite_myself_to(api_config, world_id, instance_id).await {
Ok(_) => {
println!("✅ Successfully invited yourself using automatic invite!");
println!("📍 Target: {world_id}:{instance_id}");
Ok(())
}
Err(e) => {
match &e {
vrchatapi::apis::Error::ResponseError(response_content) => {
let status = response_content.status;
let content = &response_content.content;
match status.as_u16() {
400 => {
Err(anyhow::anyhow!(
"Failed to invite myself to instance: error in response: status code 400 Bad Request\n\
Possible causes:\n\
- Instance is friends-only and you're not friends with the instance creator\n\
- Instance is invite-only\n\
- Instance has reached maximum capacity\n\
- Invalid instance format or world doesn't exist\n\
Response body: {}", content
))
}
401 => {
Err(anyhow::anyhow!(
"Failed to invite myself to instance: Unauthorized (401)\n\
Please check your authentication credentials."
))
}
403 => {
Err(anyhow::anyhow!(
"Failed to invite myself to instance: Forbidden (403)\n\
You don't have permission to join this instance."
))
}
404 => {
Err(anyhow::anyhow!(
"Failed to invite myself to instance: Not Found (404)\n\
The world or instance doesn't exist."
))
}
_ => {
Err(anyhow::anyhow!(
"Failed to invite myself to instance: HTTP {} - {}\n\
Response body: {}", status, status.canonical_reason().unwrap_or("Unknown"), content
))
}
}
}
_ => Err(anyhow::anyhow!(
"Failed to invite myself to instance: {}",
e
)),
}
}
}
}