Process(RestBot b, Dictionary <string, string> Parameters) { UUID agentKey; try { bool check = false; if (Parameters.ContainsKey("key")) { check = UUID .TryParse(Parameters["key"].ToString().Replace("_", " "), out agentKey); } else { return("<error>arguments</error>"); } if (check) { bool response = getOnline(b, agentKey); return($"<online>{response.ToString()}</online>"); } else { return("<error>unknown</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return("<error>parsekey</error>"); } }
} // end ParseArguments /// <summary> /// Register all RestPlugins to the RestBot static plugin dictionary /// </summary> /// <param name="assembly">Given assembly to search for</param> static void RegisterAllCommands(Assembly assembly) { foreach (Type t in assembly.GetTypes()) { try { if (t.IsSubclassOf(typeof(RestPlugin))) { ConstructorInfo?info = t.GetConstructor(Type.EmptyTypes); // ask for parameter-less constructor for this class, if it exists (gwyneth 20220425) if (info == null) { // Not a serious warning, some plugins might be incorrectly configured but still work well DebugUtilities.WriteWarning($"Couldn't get constructor without parameters for plugin {t.GetType().Name}!"); } else { RestPlugin plugin = (RestPlugin)info.Invoke(new object[0]); RestBot.AddPlugin(plugin); } } } catch (Exception e) { DebugUtilities.WriteError(e.Message); } } }
/// <summary> /// Get rid of a specific session. /// </summary> /// <remarks>Also calls the garbage collector after a successful bot logout (gwyneth 20220411)</remarks> /// <param name="key">Session UUID</param> public static void DisposeSession(UUID key) { DebugUtilities.WriteDebug($"Disposing of session {key.ToString()}"); if (Sessions != null) { if (!Sessions.ContainsKey(key)) { return; } Session s = Sessions[key]; if (s != null && s.Bot != null) // should never happen, we checked before { if (s.StatusCallback != null) { s.Bot.OnBotStatus -= s.StatusCallback; } s.Bot.Client.Network.Logout(); // Run garbage collector every time a bot logs out. CollectGarbage(); } else { DebugUtilities.WriteError($"Weird error in logging out session {key.ToString()} - it was on the Sessions dictionary, but strangely without a 'bot attached"); } Sessions.Remove(key); } else { DebugUtilities.WriteError($"DisposeSession called on {key.ToString()}, but we have no Sessions dictionary!"); } } // end DisposeSession()
Process(RestBot b, Dictionary <string, string> Parameters) { UUID folderID; DebugUtilities.WriteDebug("Entering folder key parser"); try { bool check = false; if (Parameters.ContainsKey("key")) { DebugUtilities.WriteDebug("Attempting to parse from POST"); check = UUID .TryParse(Parameters["key"].ToString().Replace("_", " "), out folderID); DebugUtilities.WriteDebug("Succesfully parsed POST"); } else { folderID = UUID.Zero; // start with root folder check = true; } if ( check // means that we have a correctly parsed key OR no key ) // which is fine too (attempts root folder) { DebugUtilities.WriteDebug("Entering loop"); Manager = b.Client.Inventory; Inventory = Manager.Store; StringBuilder response = new StringBuilder(); InventoryFolder startFolder = new InventoryFolder(folderID); if (folderID == UUID.Zero) { startFolder = Inventory.RootFolder; } PrintFolder(b, startFolder, response); DebugUtilities.WriteDebug("Complete"); return($"<inventory>{response}</inventory>"); } else { return($"<error>{MethodName}: parsekey</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
/// <summary> /// Handler event for this plugin. /// </summary> /// <param name="b">A currently active RestBot</param> /// <param name="Parameters">A dictionary containing the group UUID and the message to send to group IM</param> /// <returns>XML with information on the sent group IM message, if successful; /// XML error otherwise</returns> public override string Process(RestBot b, Dictionary <string, string> Parameters) { UUID groupUUID; string message; try { bool check = false; if (Parameters.ContainsKey("key")) { check = UUID.TryParse(Parameters["key"].ToString().Replace("_", " "), out groupUUID); } else { return("<error>arguments: no key</error>"); } if (check) { if (Parameters.ContainsKey("message")) { message = Parameters["message"].ToString().Replace("%20", " ").Replace("+", " "); } else { return("<error>arguments: no message</error>"); } } else { return("<error>parsekey</error>"); } message = message.TrimEnd(); if (message.Length > 1023) { message = message.Remove(1023); DebugUtilities.WriteDebug(session + " " + MethodName + " Message truncated at 1024 characters"); } string response = sendIMGroup(b, groupUUID, message); if (string.IsNullOrEmpty(response)) { return("<error>group message not sent, or answer was empty</error>"); } return("<message>" + response.Trim() + "</message>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return("<error>loads of errors</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { uint regionX, regionY; Utils .LongToUInts(b.Client.Network.CurrentSim.Handle, out regionX, out regionY); try { string target = String.Empty; if (Parameters.ContainsKey("target")) { target = Parameters["target"] .ToString() .Replace("%20", " ") .Replace("+", " "); } else { return($"<error>{MethodName}: missing argument for target</error>"); } if (target.Length == 0 || target == "off") { Active = false; targetLocalID = 0; b.Client.Self.AutoPilotCancel(); return($"<{MethodName}>off</{MethodName}>"); } else { if (Follow(target)) { return($"<{MethodName}>on</{MethodName}>"); } else { return($"<error>cannot follow {target}</error>"); } } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { try { return($"<{MethodName}><CurrentSim>{b.Client.Network.CurrentSim.ToString()}</CurrentSim><Position>{b.Client.Self.SimPosition.X},{b.Client.Self.SimPosition.Y},{b.Client.Self.SimPosition.Z}</Position></{MethodName}>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { try { b.Client.Self.Stand(); return($"<{MethodName}>standing</{MethodName}>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
public override string Process(RestBot b, Dictionary <string, string> Parameters) { try { string type = String.Empty; bool check = true; float radius = 0.0f; if (Parameters.ContainsKey("type")) { type = Parameters["type"].ToString().Replace("+", " "); } else { check = false; } if (Parameters.ContainsKey("radius")) { check &= float.TryParse(Parameters["radius"], out radius); } else { check = false; } if (!check) { return("<error>parameters have to be type, radius</error>"); } // *** get current location *** Vector3 location = b.Client.Self.SimPosition; Primitive found = b.Client.Network.CurrentSim.ObjectsPrimitives.Find( delegate(Primitive prim) { return(prim.Properties.Name == type); }); return($"<nearby_prim><pos>{found.Position.X},{found.Position.Y},{found.Position.Z}</pos></nearby_prim>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { try { return(String .Format("<position>{0},{1},{2}</position>", b.Client.Self.SimPosition.X, b.Client.Self.SimPosition.Y, b.Client.Self.SimPosition.Z)); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{e.Message}</error>"); } }
/// <summary> /// Handler event for this plugin. /// </summary> /// <param name="b">A currently active RestBot</param> /// <param name="Parameters">A dictionary containing the group name, or UUID</param> /// <returns>XML containing the group name that was activated, if successful; XML error otherwise.</returns> public override string Process(RestBot b, Dictionary <string, string> Parameters) { UUID groupUUID; string groupName; DebugUtilities.WriteDebug("Entering group key parser"); try { if (Parameters.ContainsKey("name")) { groupName = Parameters["name"].ToString().Replace("%20", " ").Replace("+", " "); } else { return($"<error>{MethodName}: arguments</error>"); } DebugUtilities.WriteDebug("Activating group"); groupUUID = GroupName2UUID(b, groupName); if (UUID.Zero != groupUUID) { string response = activateGroup(b, groupUUID); DebugUtilities.WriteDebug("Complete"); if (response != null) { return($"<active>{response.Trim()}</active>"); } else { return($"<error>{MethodName}: group could not be activated</error>"); } } else { DebugUtilities.WriteDebug($"Error: group {groupName} doesn't exist"); return($"<error>{MethodName}: group name '{groupName}' doesn't exist.</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: parsekey</error>"); } }
/// <summary> /// Handler event for this plugin. /// </summary> /// <param name="b">A currently active RestBot</param> /// <param name="Parameters">A dictionary containing the group UUID</param> /// <returns>XML with information on the activated group key if successful, /// XML error otherwise</returns> public override string Process(RestBot b, Dictionary <string, string> Parameters) { UUID groupUUID; DebugUtilities.WriteDebug("Entering group key parser"); try { bool check = false; if (Parameters.ContainsKey("key")) { DebugUtilities.WriteDebug("Attempting to parse from POST"); check = UUID.TryParse(Parameters["key"].ToString().Replace("_", " "), out groupUUID); DebugUtilities.WriteDebug("Succesfully parsed POST"); } else { return($"<error>{MethodName}: invalid arguments</error>"); } if (check) { DebugUtilities.WriteDebug("Activating group"); string?response = activateGroup(b, groupUUID); DebugUtilities.WriteDebug("Complete"); if (response != null) { return($"<active>{response.Trim()}</active>"); } else { return($"<error>{MethodName}: group could not be activated</error>"); } } else { return($"<error>{MethodName}: parsekey</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: parsekey</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { /// <summary>UUID for the prim/object that we intend the bot to touch</summary> UUID touchTargetID = UUID.Zero; DebugUtilities.WriteDebug($"{b.sessionid} {MethodName} - Searching for prim to touch"); try { bool check = false; if (Parameters.ContainsKey("prim")) { check = UUID .TryParse(Parameters["prim"].ToString().Replace("_", " "), out touchTargetID); } if (!check) { return("<error>prim to touch not specified</error>"); } // If we get to this point means that we have a correctly parsed key for the target prim DebugUtilities.WriteDebug($"{b.sessionid} {MethodName} - Trying to touch {touchTargetID.ToString()}..."); Primitive targetPrim = b.Client.Network.CurrentSim.ObjectsPrimitives.Find( prim => prim.ID == touchTargetID ); if (targetPrim != null) { b.Client.Self.Touch(targetPrim.LocalID); return($"<{MethodName}>touching {targetPrim.ID.ToString()} ({targetPrim.LocalID})</{MethodName}>"); } return($"<error>no prim with UUID {touchTargetID} found</error>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { UUID agentKey; try { bool check = false; if (Parameters.ContainsKey("key")) { check = UUID .TryParse(Parameters["key"].ToString().Replace("_", " "), out agentKey); } else { return("<error>arguments</error>"); } if (check) { string?response = getGroups(b, agentKey); // string can be null if (response == null) { return("<error>not found</error>"); } else { return(response); } } else { return("<error>unknown</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return("<error>parsekey</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { UUID agentKey; DebugUtilities.WriteDebug("Entering avatarname parser"); try { bool check = false; if (Parameters.ContainsKey("key")) { DebugUtilities.WriteDebug("Attempting to parse from POST"); check = UUID .TryParse(Parameters["key"].ToString().Replace("_", " "), out agentKey); DebugUtilities.WriteDebug("Succesfully parsed POST"); } else { return("<error>arguments</error>"); } if (check) { DebugUtilities.WriteDebug("Parsing name"); string response = getName(b, agentKey); DebugUtilities.WriteDebug("Parsed name"); DebugUtilities.WriteDebug("Complete"); return($"<name>{response.Trim()}</name>"); } else { return("<error>parsekey</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return("<error>parsekey</error>"); } }
/// <summary> /// Process a request (assuming it exists) /// </summary> /// <param name="headers">Request headers (including path, etc.)</param> /// <param name="body">Request body (will usually have all parameters from POST)</param> public static string DoProcessing(RequestHeaders headers, string body) { // Abort if we don't even have a valid configuration; too many things depend on it... (gwyneth 20220213) if (Program.config == null) { return("<error>No valid configuration loaded, aborting</error>"); } //Setup variables DebugUtilities.WriteInfo($"New request - {headers.RequestLine.Path}"); //Split the URL string[] parts = headers.RequestLine.Path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (parts.Length < 1) { return("<error>invalidmethod</error>"); } string Method = parts[0]; /// <summary>Process the request params from POST, URL</summary> Dictionary <string, string> Parameters = RestBot.HandleDataFromRequest(headers, body); string debugparams = String.Empty; string debugparts = String.Empty; foreach (KeyValuePair <string, string> kvp in Parameters) { debugparams = debugparams + "[" + kvp.Key + "=" + kvp.Value + "] "; } DebugUtilities.WriteDebug($"Parameters (total: {Parameters.Count()}) - '{debugparams}'"); foreach (string s in parts) { debugparts = debugparts + "[ " + s + " ]"; } DebugUtilities.WriteDebug($"Parts (total: {parts.Count()}) - '{debugparts}'"); if (Method == "establish_session") { DebugUtilities.WriteDebug("We have an `establish_session` method."); // Alright, we're going to try to establish a session // Start location is optional (gwyneth 20220421) if (parts.Length >= 2 && parts[1] == Program.config.security.serverPass && Parameters.ContainsKey("first") && Parameters.ContainsKey("last") && Parameters.ContainsKey("pass")) { DebugUtilities.WriteDebug("Found required parameters for establish_session"); if (Sessions != null) { foreach (KeyValuePair <UUID, Session> ss in Sessions) { if (ss.Value != null && ss.Value.Bot != null) { DebugUtilities.WriteSpecial($"Avatar check: [{ss.Value.Bot.First.ToLower()}/{ss.Value.Bot.Last.ToLower()}] = [{Parameters["first"].ToLower()}/{Parameters["last"].ToLower()}]"); if (Parameters["first"].ToLower() == ss.Value.Bot.First.ToLower() && Parameters["last"].ToLower() == ss.Value.Bot.Last.ToLower() ) { DebugUtilities.WriteWarning($"Already running avatar {Parameters["first"]} {Parameters["last"]}"); /// <value>Temporary string to construct a full response, if possible; if not, we catch the /// exception and return a much shorter version</value> /// <remarks>This is a hack. The issue is that we're probably acessing nullable /// elements without checking. (gwyneth 20220428)</remarks> string returnString = ""; try { // Attempt to get a returnString = $@"<existing_session>true</existing_session> <session_id>{ss.Key.ToString()}</session_id> <key>{ss.Value.Bot.Client.Self.AgentID.ToString()}</key> <name>{ss.Value.Bot.Client.Self.FirstName} {ss.Value.Bot.Client.Self.LastName}</name> <FirstName>{ss.Value.Bot.Client.Self.FirstName}</FirstName> <LastName>{ss.Value.Bot.Client.Self.LastName}</LastName> <status>{ss.Value.Bot.myStatus.ToString()}</status> <uptime>{ss.Value.Bot.getUptimeISO8601()}</uptime> <start>{ss.Value.Bot.Start}</start> <CurrentSim>{ss.Value.Bot.Client.Network.CurrentSim.ToString()}</CurrentSim> <Position>{ss.Value.Bot.Client.Self.SimPosition.X},{ss.Value.Bot.Client.Self.SimPosition.Y},{ss.Value.Bot.Client.Self.SimPosition.Z}</Position> <Rotation>{ss.Value.Bot.Client.Self.SimRotation.X},{ss.Value.Bot.Client.Self.SimRotation.Y},{ss.Value.Bot.Client.Self.SimRotation.Z},{ss.Value.Bot.Client.Self.SimRotation.W}</Rotation> "; } catch (Exception e) { DebugUtilities.WriteError($"Could not generate full response, error was: '{e.Message}'; falling back to the simple, minimalistic answer"); returnString = $"<existing_session>true</existing_session><session_id>{ss.Key.ToString()}</session_id>"; } return(returnString); } } } } else { DebugUtilities.WriteDebug("No available sessions..."); } UUID id = UUID.Random(); Session s = new Session(); s.ID = id; s.Hostname = headers.Hostname; s.LastAccessed = DateTime.Now; // Needs the $1$ for the md5 on the login for LibreMetaverse if (!Parameters["pass"].StartsWith("$1$")) { Parameters["pass"] = "******" + Parameters["pass"]; } // check if user has provided us with a starting location (default is to use the last location) // (gwyneth 20220420) string gridLocation = Parameters.ContainsKey("start") ? Parameters["start"] : "last"; s.Bot = new RestBot(s.ID, Parameters["first"], Parameters["last"], Parameters["pass"], gridLocation); if (Sessions != null) { lock (Sessions) { Sessions.Add(id, s); } } else { // no "else", we have no dictionary DebugUtilities.WriteWarning("Possible issue: we have null Sessions when adding, which shouldn't happen"); } RestBot.LoginReply reply = s.Bot.Login(); if (reply.wasFatal) { if (Sessions != null) { lock (Sessions) { if (Sessions.ContainsKey(id)) { Sessions.Remove(id); } } } else { // no "else", we have no dictionary DebugUtilities.WriteWarning("Possible issue: we have null Sessions when removing, which shouldn't happen"); } } return(reply.xmlReply); } else { String result = String.Empty; if (parts.Length < 2) { result = "Missing a part."; } if (!Parameters.ContainsKey("first")) { result = result + " Missing 'first' arg."; } if (!Parameters.ContainsKey("last")) { result = result + " Missing 'last' arg."; } if (!Parameters.ContainsKey("pass")) { result = result + " Missing 'pass' arg."; } return($"<error>arguments: {result}</error>"); } } // Note: formerly undocumented functionality!! (gwyneth 20220414) else if (Method == "server_quit") { if (parts.Length < 2) { return($"<error>{Method}: missing 'pass' arg.</error>"); } if (parts[1] == Program.config.security.serverPass) { if (Sessions != null) { foreach (KeyValuePair <UUID, Session> s in Sessions) { lock (Sessions) DisposeSession(s.Key); } StillRunning = false; // note: a caveat of this undocumented method is that it requires a _new_ // incoming request to actually kill the server... could be a ping, though. (gwyneth 20220414) return("<status>success - all bot sessions were logged out and a request was made for queued shutdown</status>"); } else { // it's fine if there are no sessions (gwyneth 20220414) return("<status>success - no sessions were active</status>"); } } else { // wrong password sent! (gwyneth 20220414) return($"<error>{Method}: server authentication failure</error>"); } } else if (Method == "ping") { if (parts.Length < 2) { return($"<error>{Method}: missing 'pass' arg.</error>"); } if (parts[1] == Program.config.security.serverPass) { return($"<{Method}>I'm alive!</{Method}>"); } else { // wrong password sent! (gwyneth 20220414) return($"<error>{Method}: server authentication failure</error>"); } } else if (Method == "session_list") { if (parts.Length < 2) { return("<error>missing 'pass' arg.</error>"); } if (parts[1] == Program.config.security.serverPass) { bool check = false; if (Program.Sessions.Count != 0) // no sessions? that's fine, no need to abort { check = true; } string response = $"<{Method}>"; if (check) // optimisation: if empty, no need to run the foreach (gwyneth 20220424) { foreach (KeyValuePair <OpenMetaverse.UUID, RESTBot.Session> kvp in Program.Sessions) { if (kvp.Value.Bot != null && kvp.Value.Bot.Client != null && kvp.Value.Bot.Client.Self != null && kvp.Value.Bot.Client.Network != null) { response += $@" <session> <session_id>{kvp.Key.ToString()}</session_id> <key>{kvp.Value.Bot.Client.Self.AgentID.ToString()}</key> <name>{kvp.Value.Bot.Client.Self.FirstName} {kvp.Value.Bot.Client.Self.LastName}</name> <FirstName>{kvp.Value.Bot.Client.Self.FirstName}</FirstName> <LastName>{kvp.Value.Bot.Client.Self.LastName}</LastName> <status>{kvp.Value.Bot.myStatus.ToString()}</status> <uptime>{kvp.Value.Bot.getUptimeISO8601()}</uptime> <start>{kvp.Value.Bot.Start}</start> <CurrentSim>{kvp.Value.Bot.Client.Network.CurrentSim.ToString()}</CurrentSim> <Position>{kvp.Value.Bot.Client.Self.SimPosition.X},{kvp.Value.Bot.Client.Self.SimPosition.Y},{kvp.Value.Bot.Client.Self.SimPosition.Z}</Position> <Rotation>{kvp.Value.Bot.Client.Self.SimRotation.X},{kvp.Value.Bot.Client.Self.SimRotation.Y},{kvp.Value.Bot.Client.Self.SimRotation.Z},{kvp.Value.Bot.Client.Self.SimRotation.W}</Rotation> </session>" ; } else { // Somehow, we have a session ID that has no bot assigned; // this should never be the case, but... (gwyneth 20220426) response += $"<session><session_id>{kvp.Key.ToString()}</session_id><key>{UUID.Zero.ToString()}</key></session>"; } } } else { response += "no sessions"; } response += $"</{Method}>"; return(response); } else { // wrong password sent! (gwyneth 20220414) return($"<error>{Method}: server authentication failure</error>"); } } else if (Method == "stats") { if (parts.Length < 2) { return($"<error>{Method}: missing 'pass' arg.</error>"); } if (parts[1] == Program.config.security.serverPass) { string response = "<stats><bots>" + ((Sessions != null) ? Sessions.Count.ToString() : "0") + "</bots>" + "<uptime>" + (DateTime.Now - uptime) + "</uptime></stats>"; return(response); } else { return($"<error>{Method}: server authentication failure</error>"); } } //Only a method? pssh. if (parts.Length == 1) { return("<error>no session key found</error>"); } UUID sess = new UUID(); try { sess = new UUID(parts[1]); } catch (FormatException) { return("<error>cannot parse the session key</error>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); } //Session checking if (!ValidSession(sess, headers.Hostname)) { return("<error>invalidsession</error>"); } //YEY PROCESSING RestBot?r = null; if (Sessions != null) { r = Sessions[sess].Bot; } if (r == null) { return($"<error>no RestBot found for session {sess.ToString()}</error>"); } //Last accessed for plugins if (Sessions != null) { Sessions[sess].LastAccessed = DateTime.Now; } //Pre-error checking if (r.myStatus != RestBot.Status.Connected) //Still logging in? { return($"<error>{r.myStatus.ToString()}</error>"); } else if (!r.Client.Network.Connected) //Disconnected? { return("<error>clientdisconnected</error>"); } else if (Method == "exit") { DisposeSession(sess); return("<disposed>true</disposed>"); } else if (Method == "stats") { string response = "<bots>" + ((Sessions != null) ? Sessions.Count.ToString() : "NaN") + "</bots>"; response += "<uptime>" + (DateTime.Now - uptime) + "</uptime>"; return(response); } return(r.DoProcessing(Parameters, parts)); } // end DoProcessing
/// <summary> /// Handler event for this plugin. /// </summary> /// <param name="b">A currently active RestBot</param> /// <param name="Parameters">A dictionary containing the message </param> /// <returns></returns> public override string Process(RestBot b, Dictionary <string, string> Parameters) { string message; string subject; UUID groupUUID = UUID.Zero; UUID attachmentUUID = UUID.Zero; GroupNotice notice; try { if (Parameters.ContainsKey("subject")) { subject = Parameters["subject"].ToString().Replace("%20", " ").Replace("+", " "); } else { return("<error>No notice subject</error>"); } if (Parameters.ContainsKey("message")) { message = Parameters["message"].ToString().Replace("%20", " ").Replace("+", " "); } else { return("<error>No notice message</error>"); } if (Parameters.ContainsKey("group")) { if (!UUID.TryParse(Parameters["group"].ToString().Replace("_", " "), out groupUUID)) { return("<error>parsekey group</error>"); } } else { return("<error>arguments: no group key</error>"); } if (Parameters.ContainsKey("attachment")) { if (!UUID.TryParse(Parameters["attachment"].ToString().Replace("_", " "), out attachmentUUID)) { return("<error>parsekey attachment</error>"); } } else { // just a warning, attachment can be empty DebugUtilities.WriteWarning(session + " " + MethodName + " Notice has no attachment (no problem)"); } DebugUtilities.WriteDebug(session + " " + MethodName + " Attempting to create a notice"); /* This doesn't work as it should! * if (!b.Client.Inventory.Store.Contains(attachmentUUID)) * { * DebugUtilities.WriteWarning(session + " " + MethodName + " Item UUID " + attachmentUUID.ToString() + " not found on inventory (are you using an Asset UUID by mistake?)"); * attachmentUUID = UUID.Zero; * } */ notice = new GroupNotice(); notice.Subject = subject; notice.Message = message; notice.AttachmentID = attachmentUUID; // this is the inventory UUID, not the asset UUID notice.OwnerID = b.Client.Self.AgentID; b.Client.Groups.SendGroupNotice(groupUUID, notice); DebugUtilities.WriteDebug($"{session} {MethodName} Sent Notice from avatar {notice.OwnerID.ToString()} to group: {groupUUID.ToString()} subject: '{notice.Subject.ToString()}' message: '{notice.Message.ToString()}' Optional attachment: {notice.AttachmentID.ToString()} Serialisation: {Utils.BytesToString(notice.SerializeAttachment())}"); return("<notice>sent</notice>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return("<error>loads of errors</error>"); } }
public override string Process(RestBot b, Dictionary <string, string> Parameters) { try { string type = String.Empty; bool check = true; float radius = 0.0f; if (Parameters.ContainsKey("type")) { type = Parameters["type"].ToString().Replace("+", " "); } else { check = false; } if (Parameters.ContainsKey("radius")) { check &= float.TryParse(Parameters["radius"], out radius); } else { check = false; } if (!check) { return("<error>parameters have to be type, radius</error>"); } // *** get current location *** Vector3 location = b.Client.Self.SimPosition; // *** find all objects in radius *** List <Primitive> prims = b.Client.Network.CurrentSim.ObjectsPrimitives.FindAll( delegate(Primitive prim) { Vector3 pos = prim.Position; return((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius)); } ); // *** request properties of these objects *** bool complete = RequestObjectProperties(prims, 0); String resultSet = String.Empty; foreach (Primitive p in prims) { string?name = p.Properties != null ? p.Properties.Name : null; if (String.IsNullOrEmpty(type) || ((name != null) && (name.Contains(type)))) { resultSet += $"<prim><name>{name}</name><pos>{p.Position.X},{p.Position.Y},{p.Position.Z}</pos><id>{p.ID}</id></prim>"; } } return("<nearby_prims>" + resultSet + "</nearby_prims>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{e.Message}</error>"); } }
/// <summary> /// Login block /// </summary> public LoginReply Login() { LoginReply response = new LoginReply(); DebugUtilities.WriteSpecial("Login block was called in Login()"); if (Client.Network.Connected) { DebugUtilities.WriteError("Uhm, Login() was called when we where already connected. Hurr"); return(new LoginReply()); } //Client.Network.LoginProgress += // delegate(object? sender, LoginProgressEventArgs e) // { // DebugUtilities.WriteDebug($"Login {e.Status}: {e.Message}"); // if (e.Status == LoginStatus.Success) // { // DebugUtilities.WriteSpecial("Logged in successfully"); // myStatus = Status.Connected; // response.wasFatal = false; // response.xmlReply = "<success><session_id>" + sessionid.ToString() + "</session_id></success>"; // } // else if (e.Status == LoginStatus.Failed) // { // DebugUtilities.WriteError("$There was an error while connecting: {Client.Network.LoginErrorKey}"); // response.wasFatal = true; // response.xmlReply = "<error></error>"; // } // }; // Optimize the throttle Client.Throttle.Wind = 0; Client.Throttle.Cloud = 0; Client.Throttle.Land = 1000000; Client.Throttle.Task = 1000000; // we add this check here because LOGIN_SERVER should never be assigned null (gwyneth 20220213) if (Program.config != null && Program.config.networking.loginuri != null) { Client.Settings.LOGIN_SERVER = Program.config.networking.loginuri; // could be String.Empty, so we check below... } else if (RESTBot.XMLConfig.Configuration.defaultLoginURI != null) { Client.Settings.LOGIN_SERVER = RESTBot.XMLConfig.Configuration.defaultLoginURI; // could ALSO be String.Empty, so we check below... } else { Client.Settings.LOGIN_SERVER = String.Empty; } // Any of the above _might_ have set LOGIN_SERVER to an empty string, so we check first if we have // something inside the string. (gwyneth 20220213) // To-do: validate the URL first? It's not clear if .NET 6 already does that at some point... if (Client.Settings.LOGIN_SERVER == String.Empty) { // we don't know where to login to! response.wasFatal = true; response.xmlReply = "<error fatal=\"true\">No login URI provided</error>"; DebugUtilities.WriteError("No login URI provided; aborting..."); return(response); } DebugUtilities.WriteDebug($"Login URI: {Client.Settings.LOGIN_SERVER}"); LoginParams loginParams = Client.Network.DefaultLoginParams(First, Last, MD5Password, "RestBot", Program.Version); loginParams.Start = Start; if (Client.Network.Login(loginParams)) { DebugUtilities.WriteSpecial($"{First} {Last} logged in successfully"); myStatus = Status.Connected; response.wasFatal = false; response.xmlReply = $@"<success> <session_id>{sessionid.ToString()}</session_id> <key>{Client.Self.AgentID.ToString()}</key> <name>{Client.Self.FirstName} {Client.Self.LastName}</name> <FirstName>{Client.Self.FirstName}</FirstName> <LastName>{Client.Self.LastName}</LastName> <CurrentSim>{Client.Network.CurrentSim.ToString()}</CurrentSim> <Position>{Client.Self.SimPosition.X},{Client.Self.SimPosition.Y},{Client.Self.SimPosition.Z}</Position> <Rotation>{Client.Self.SimRotation.X},{Client.Self.SimRotation.Y},{Client.Self.SimRotation.Z},{Client.Self.SimRotation.W}</Rotation> </success>"; } else { DebugUtilities .WriteError($"There was an error while connecting: {Client.Network.LoginErrorKey}"); switch (Client.Network.LoginErrorKey) { case "connect": case "key": case "disabled": response.wasFatal = true; response.xmlReply = $"<error fatal=\"true\">{Client.Network.LoginMessage}</error>"; break; case "presence": case "timed out": case "god": DebugUtilities .WriteWarning("Nonfatal error while logging in.. this may be normal"); response.wasFatal = false; response.xmlReply = $"<error fatal=\"false\">{Client.Network.LoginMessage}</error><retry>10</retry><session_id>{sessionid}</session_id>"; DebugUtilities .WriteSpecial("Relogin attempt will be made in 10 minutes"); ReloginTimer.Interval = 10 * 60 * 1000; //10 minutes ReloginTimer.Start(); break; default: DebugUtilities .WriteError($"{sessionid.ToString()} UNKNOWN ERROR {Client.Network.LoginErrorKey} WHILE ATTEMPTING TO LOGIN"); response.wasFatal = true; response.xmlReply = $"<error fatal=\"true\">Unknown error '{Client.Network.LoginErrorKey}' has occurred.</error>"; break; } if (response.wasFatal == false) { myStatus = Status.Reconnecting; } } //Client.Network.BeginLogin(loginParams); return(response); } // end Login()
Process(RestBot b, Dictionary <string, string> Parameters) { try { string sim = ""; float x = 128.0f, y = 128.0f, z = 30.0f; bool check = true; if (Parameters.ContainsKey("sim")) { sim = Parameters["sim"].ToString(); } else { check = false; } if (Parameters.ContainsKey("x")) { check &= float.TryParse(Parameters["x"], out x); } else { check = false; } if (Parameters.ContainsKey("y")) { check &= float.TryParse(Parameters["y"], out y); } else { check = false; } if (Parameters.ContainsKey("z")) { check &= float.TryParse(Parameters["z"], out z); } else { check = false; } if (!check) { return($"<error>{MethodName} parameters have to be simulator name, x, y, z</error>"); } // debug: calculate destination vector, show data, just to make sure it's correct (gwyneth 20220411) Vector3 teleportPoint = new Vector3(x, y, z); DebugUtilities.WriteDebug($"attempting teleport to ({x}), ({y}), ({z}) - vector: {teleportPoint.ToString()}"); if (b.Client.Self.Teleport(sim, teleportPoint)) { if (b.Client.Network.CurrentSim != null) { return(String .Format("<teleport><CurrentSim>{0}</CurrentSim><Position>{1},{2},{3}</Position></teleport>", b.Client.Network.CurrentSim.ToString(), b.Client.Self.SimPosition.X, b.Client.Self.SimPosition.Y, b.Client.Self.SimPosition.Z)); } else { return($"<error>Teleport failed, no sim handle found: {b.Client.Self.TeleportMessage}</error>"); } } else { return($"<error>Teleport failed: {b.Client.Self.TeleportMessage}</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { UUID embedItemID = UUID.Zero; string notecardName, notecardData; DebugUtilities.WriteDebug("Entering key parser"); try { // item ID to embed is optional; handle later if (Parameters.ContainsKey("key")) { UUID .TryParse(Parameters["key"].ToString().Replace("_", " "), out embedItemID); } // notecard data is required! if (Parameters.ContainsKey("notecard")) { DebugUtilities .WriteDebug("Attempting to parse notecard data from POST"); notecardData = Parameters["notecard"]; } else { return("<error>notecard text data not found</error>"); } // notecard name is optional, we'll assign a random name if (Parameters.ContainsKey("name")) { DebugUtilities .WriteDebug("Attempting to parse notecard name from POST"); notecardName = Parameters["name"]; DebugUtilities.WriteDebug("Succesfully parsed POST"); } else { notecardName = "(no name)"; } UUID notecardItemID = UUID.Zero, notecardAssetID = UUID.Zero; bool success = false, finalUploadSuccess = false; string message = String.Empty; AutoResetEvent notecardEvent = new AutoResetEvent(false); DebugUtilities .WriteDebug($"Notecard data ('{notecardName}') found: '{notecardData}'"); #region Notecard asset data AssetNotecard notecard = new AssetNotecard(); notecard.BodyText = notecardData; // Item embedding if (embedItemID != UUID.Zero) { // Try to fetch the inventory item InventoryItem?item = FetchItem(b, embedItemID); if (item != null) { notecard.EmbeddedItems = new List <InventoryItem> { item }; notecard.BodyText += (char)0xdbc0 + (char)0xdc00; } else { return("Failed to fetch inventory item " + embedItemID); } } notecard.Encode(); #endregion Notecard asset data b .Client .Inventory .RequestCreateItem(b .Client .Inventory .FindFolderForType(AssetType.Notecard), notecardName, notecardName + " created by LibreMetaverse RESTbot " + DateTime.Now, AssetType.Notecard, UUID.Random(), InventoryType.Notecard, PermissionMask.All, delegate(bool createSuccess, InventoryItem item) { if (createSuccess) { #region Upload an empty notecard asset first AutoResetEvent emptyNoteEvent = new AutoResetEvent(false); AssetNotecard empty = new AssetNotecard(); empty.BodyText = "\n"; empty.Encode(); b .Client .Inventory .RequestUploadNotecardAsset(empty.AssetData, item.UUID, delegate( bool uploadSuccess, string status, UUID itemID, UUID assetID) { notecardItemID = itemID; notecardAssetID = assetID; success = uploadSuccess; message = status ?? "Unknown error uploading notecard asset"; emptyNoteEvent.Set(); }); emptyNoteEvent.WaitOne(NOTECARD_CREATE_TIMEOUT, false); #endregion Upload an empty notecard asset first if (success) { // Upload the actual notecard asset b .Client .Inventory .RequestUploadNotecardAsset(notecard.AssetData, item.UUID, delegate(bool uploadSuccess, string status, UUID itemID, UUID assetID) { notecardItemID = itemID; notecardAssetID = assetID; finalUploadSuccess = uploadSuccess; message = status ?? "Unknown error uploading notecard asset"; notecardEvent.Set(); }); } else { notecardEvent.Set(); } } else { message = "Notecard item creation failed"; notecardEvent.Set(); } }); // end delegate // end RequestCreateItem notecardEvent.WaitOne(NOTECARD_CREATE_TIMEOUT, false); // DebugUtilities.WriteDebug("Notecard possibly created, ItemID " + notecardItemID + " AssetID " + notecardAssetID + " Content: '" + DownloadNotecard(b, notecardItemID, notecardAssetID) + "'"); if (finalUploadSuccess) { DebugUtilities .WriteDebug($"Notecard successfully created, ItemID {notecardItemID}; AssetID {notecardAssetID}; Content: '{DownloadNotecard(b, notecardItemID, notecardAssetID)}'"); return($"<notecard><ItemID>{notecardItemID}</ItemID><AssetID>{notecardAssetID}</AssetID><name>{notecardName}</name></notecard>"); } else { return($"<error>Notecard creation failed: {message}</error>"); } } // end try catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { /// <summary>UUID for the prim/object that we intend the bot to sit on</summary> UUID sitTargetID = UUID.Zero; DebugUtilities.WriteDebug($"{b.sessionid} {MethodName} - Searching for prim to sit on"); try { bool check = false; /// <summary>optionally, we can skip prim search and force sitting on UUID</summary> bool force = false; if (Parameters.ContainsKey("target")) { check = UUID .TryParse(Parameters["target"].ToString().Replace("_", " "), out sitTargetID); } if (!check) { return($"<error>{MethodName} no sit target specified</error>"); } // optional if (Parameters.ContainsKey("force")) { check = bool.TryParse(Parameters["force"], out force); } if (!check) { return($"<error>{MethodName} force sit attempted with wrong setting; only true/false are allowed</error>"); } // If we get to this point means that we have a correctly parsed key for the target prim DebugUtilities.WriteDebug($"{b.sessionid} {MethodName} - Trying to sit on {sitTargetID.ToString()}..."); // If not forcing, we'll search for a prim with this UUID in the 'bot interest list // and retrieve the information from that prim. (gwyneth 20220410) if (!force) { Primitive targetPrim = b.Client.Network.CurrentSim.ObjectsPrimitives.Find( prim => prim.ID == sitTargetID ); if (targetPrim != null) { b.Client.Self.RequestSit(targetPrim.ID, Vector3.Zero); b.Client.Self.Sit(); return($"<{MethodName}>sitting on \"{targetPrim.Properties.Name}\" ({targetPrim.ID.ToString()} [Local ID: {targetPrim.LocalID}])</{MethodName}>"); } return($"<error>{MethodName}: no prim with UUID {sitTargetID} found</error>"); } else // forcing to sit on a prim we KNOW that exists! (gwyneth 20220410) { b.Client.Self.RequestSit(sitTargetID, Vector3.Zero); b.Client.Self.Sit(); return($"<{MethodName}>forced sitting on {sitTargetID.ToString()}</{MethodName}>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { UUID itemID; DebugUtilities.WriteDebug("List Item - Entering key parser"); try { bool check = false; if (Parameters.ContainsKey("key")) { DebugUtilities.WriteDebug("Attempting to parse from POST"); check = UUID .TryParse(Parameters["key"].ToString().Replace("_", " "), out itemID); DebugUtilities.WriteDebug("Succesfully parsed POST"); } else { return($"<error>{MethodName}: parsekey</error>"); } if ( check // means that we have a correctly parsed key ) { DebugUtilities .WriteDebug("Fetching " + itemID.ToString()); InventoryItem oneItem; oneItem = b.Client.Inventory.FetchItem(itemID, b.Client.Self.AgentID, 5000); // to-do: catch timeout explicitly if (oneItem == null) { return($"<error>item {itemID.ToString()} not found</error>"); } string response; response = $@"<item> <AssetUUID>{oneItem.AssetUUID.ToString()}</AssetUUID> <PermissionsOwner>{PermMaskString(oneItem.Permissions.OwnerMask)}</PermissionsOwner> <PermissionsGroup>{PermMaskString(oneItem.Permissions.GroupMask)}</PermissionsGroup> <AssetType>{oneItem.AssetType.ToString()}</AssetType> <InventoryType>{oneItem.InventoryType.ToString()}</InventoryType> <CreatorID>{oneItem.CreatorID.ToString()}</CreatorID> <Description>{oneItem.Description.ToString()}</Description> <GroupID>{oneItem.GroupID.ToString()}</GroupID> <GroupOwned>{oneItem.GroupOwned.ToString()}</GroupOwned> <SalePrice>{oneItem.SalePrice.ToString()}</SalePrice> <SaleType>{oneItem.SaleType.ToString()}</SaleType> <Flags>{oneItem.Flags.ToString()}</Flags> <CreationDate> {oneItem.CreationDate.ToString()} </CreationDate> <LastOwnerID>{oneItem.LastOwnerID.ToString()}</LastOwnerID> </item>"; return(response); } else { return($"<error>{MethodName}: parsekey</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { try { string name = ""; bool check = true; // "target" is essentially an avatar name (first, last) (gwyneth 20220120) if (Parameters.ContainsKey("target")) { name = Parameters["target"].ToString().Replace("+", " "); } else { check = false; } if (!check) { return("<error>parameters have to be target avatar name (first, last)</error>"); } lock (b.Client.Network.Simulators) { for (int i = 0; i < b.Client.Network.Simulators.Count; i++) { DebugUtilities .WriteDebug("Found Avatars: " + b.Client.Network.Simulators[i].ObjectsAvatars.Count); Avatar?target = b .Client .Network .Simulators[i] .ObjectsAvatars .Find(delegate(Avatar avatar) { DebugUtilities.WriteDebug($"Found avatar: {avatar.Name}"); return(avatar.Name == name); }); if (target != null) { return(String .Format("<goal_position>{0},{1},{2}</goal_position><curr_position>{3},{4},{5}</curr_position>", target.Position.X, target.Position.Y, target.Position.Z, b.Client.Self.SimPosition.X, b.Client.Self.SimPosition.Y, b.Client.Self.SimPosition.Z)); } else { DebugUtilities.WriteError($"Error obtaining the avatar: {name}"); } } } return("<error>avatar_position failed.</error>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { uint regionX, regionY; Utils .LongToUInts(b.Client.Network.CurrentSim.Handle, out regionX, out regionY); try { double x = 0.0f, y = 0.0f, z = 0.0f, distance = 0.0f; bool check = true; /// <summary>Selects between running (true) or walking (false)</summary> bool run = false; if (Parameters.ContainsKey("x")) { check &= double.TryParse(Parameters["x"], out x); DebugUtilities.WriteDebug($"{MethodName} Parse: {Parameters["x"]} into {x}"); } else { check = false; } if (Parameters.ContainsKey("y")) { check &= double.TryParse(Parameters["y"], out y); } else { check = false; } if (Parameters.ContainsKey("z")) { check &= double.TryParse(Parameters["z"], out z); } else { check = false; } if (Parameters.ContainsKey("distance")) { check &= double.TryParse(Parameters["distance"], out distance); } if (Parameters.ContainsKey("run")) { check &= bool.TryParse(Parameters["run"], out run); } if (!check) { return($"<error>{MethodName} parameters have to be x, y, z, [distance, run]</error>"); } if (run) { b.Client.Self.Movement.AlwaysRun = true; } else { b.Client.Self.Movement.AlwaysRun = false; } goalPos = new Vector3((float)x, (float)y, (float)z); b.Client.Self.Movement.TurnToward(goalPos, false); // Check for null and, if so, abort with error (gwyneth 20220213) if (me == null) { DebugUtilities.WriteError("'me' was null!"); return($"<error>{MethodName}: 'me' was null</error>"); } prevDistance = Vector3.Distance(goalPos, me.Client.Self.SimPosition); // Convert the local coordinates to global ones by adding the region handle parts to x and y b .Client .Self .AutoPilot(goalPos.X + regionX, goalPos.Y + regionY, goalPos.Z); Active = true; attempts = 20; // reset attempts, or else they'll be stuck at zero... (gwyneth 20220304) while (Active) { Thread.Sleep(1 * 1000); } DebugUtilities.WriteSpecial($"End {MethodName} Thread!"); return($"<{MethodName}>{b.Client.Self.GlobalPosition.X},{b.Client.Self.GlobalPosition.Y},{b.Client.Self.GlobalPosition.Z}</{MethodName}>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { Utils .LongToUInts(b.Client.Network.CurrentSim.Handle, out regionX, out regionY); try { double x = 0.0f, y = 0.0f, z = 0.0f; bool check = true; bool run = false; string avatarName = String.Empty; if (Parameters.ContainsKey("avatar")) { avatarName = Parameters["avatar"].ToString().Replace("+", " "); } else { check = false; } // added it here, but I'm not sure if it's correct... (gwyneth 20220409) if (Parameters.ContainsKey("run")) { check &= bool.TryParse(Parameters["run"], out run); } if (!check) { return($"<error>{MethodName} parameters have to be avatar name to follow</error>"); } if (run) { b.Client.Self.Movement.AlwaysRun = true; } else { b.Client.Self.Movement.AlwaysRun = false; } lock (b.Client.Network.Simulators) { for (int i = 0; i < b.Client.Network.Simulators.Count; i++) { DebugUtilities .WriteDebug($"Found {b.Client.Network.Simulators[i].ObjectsAvatars.Count} Avatar(s)"); target = b .Client .Network .Simulators[i] .ObjectsAvatars .Find(delegate(Avatar avatar) { DebugUtilities.WriteDebug($"Found avatar: {avatar.Name}"); return(avatar.Name == avatarName); }); if (target != null) { goalPos = target.Position; } else { DebugUtilities .WriteError($"{MethodName} - Error finding position of '{avatarName}'"); return($"<error>{MethodName} error finding position of '{avatarName}'</error>"); } } } goalPos = new Vector3((float)x, (float)y, (float)z); b.Client.Self.Movement.TurnToward(goalPos, false); // C# 8+ is stricter with nulls; if 'me' is null, we got a problem, // because there is no way to calculate the distance; so we simply set it to zero // in that case; this may play havoc with the algorithm, though. (gwyneth 20220213) if (me != null) { prevDistance = Vector3.Distance(goalPos, me.Client.Self.SimPosition); } else { prevDistance = 0; } if (prevDistance > 30) { b.Client.Self.Movement.AlwaysRun = true; } else { b.Client.Self.Movement.AlwaysRun = false; } // Convert the local coordinates to global ones by adding the region handle parts to x and y b .Client .Self .AutoPilot(goalPos.X + regionX, goalPos.Y + regionY, goalPos.Z); Active = true; while (Active) { Thread.Sleep(1 * 1000); } DebugUtilities.WriteSpecial($"End {MethodName} Thread!"); return($"<{MethodName}>{b.Client.Self.GlobalPosition.X},{b.Client.Self.GlobalPosition.Y},{b.Client.Self.GlobalPosition.Z}</{MethodName}>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
/// <summary> /// Handler event for this plugin. /// </summary> /// <param name="b">A currently active RestBot</param> /// <param name="Parameters">A dictionary containing the UUID of the avatar to invite, the UUID of /// the group to invite, and the role UUID to invite the avatar in to</param> /// <returns>XML with information about a successful invite to a group/role; XML error otherwise</returns> /// <remarks><para>Currently, avatars can only be invited to a singke role at the time.</para> /// <para>To-do: work on allowing invitations to multiple roles simultaenously!</para></remarks> public override string Process(RestBot b, Dictionary <string, string> Parameters) { UUID avatar = UUID.Zero; UUID group = UUID.Zero; UUID role = UUID.Zero; List <UUID> roles = new List <UUID>(); try { bool check = false; if (Parameters.ContainsKey("avatar")) { check = UUID.TryParse(Parameters["avatar"].ToString().Replace("_", " "), out avatar); } else { return("<error>arguments: no avatar key</error>"); } if (check) { if (Parameters.ContainsKey("group")) { check = UUID.TryParse(Parameters["group"].ToString().Replace("_", " "), out group); } else { return("<error>arguments: no group key</error>"); } // to-do: avatars can be invited to multiple roles. if (Parameters.ContainsKey("role")) { if (!UUID.TryParse(Parameters["role"].ToString().Replace("_", " "), out role)) { // just a warning, role is optional DebugUtilities.WriteDebug(session + " " + MethodName + " no role found, but that's ok"); } roles.Add(role); } else { roles.Add(UUID.Zero); // no roles to add } } else { return("<error>parsekey</error>"); } DebugUtilities.WriteDebug(session + " " + MethodName + " Group UUID: " + group + " Avatar UUID to join group: " + avatar + " Role UUID for avatar to join: " + roles.ToString() + " (NULL_KEY is fine)"); b.Client.Groups.Invite(group, roles, avatar); return("<invitation>invited " + avatar + " to " + group + "</invitation>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return("<error>loads of errors</error>"); } }
Process(RestBot b, Dictionary <string, string> Parameters) { UUID itemID, avatarKey; InventoryManager Manager; DebugUtilities.WriteDebug("Entering key parser"); try { bool check = false; if (Parameters.ContainsKey("itemID")) { DebugUtilities.WriteDebug("Attempting to parse from POST"); check = UUID .TryParse(Parameters["itemID"].ToString().Replace("_", " "), out itemID); if (check) { check = UUID .TryParse(Parameters["avatarKey"].ToString().Replace("_", " "), out avatarKey); DebugUtilities.WriteDebug("Succesfully parsed POST"); } else { return($"<error>{MethodName}: parsekey itemID</error>"); } } else { return($"<error{MethodName}:>parsekey</error>"); } if ( check // means that we have a correctly parsed key ) { DebugUtilities .WriteDebug($"Give Item {itemID.ToString()} to avatar {avatarKey.ToString()}"); // Extract item information from inventory InventoryItem oneItem; oneItem = b.Client.Inventory.FetchItem(itemID, b.Client.Self.AgentID, 5000); // to-do: catch timeout explicitly if (oneItem == null) { return($"<error>item {itemID.ToString()} not found</error>"); } // attempt to send it to the avatar Manager = b.Client.Inventory; Manager .GiveItem(oneItem.UUID, oneItem.Name, oneItem.AssetType, avatarKey, false); return($"<item><name>{oneItem.Name}</name><assetType>{oneItem.AssetType}</assetType><itemID>{itemID.ToString()}</itemID><avatarKey>{avatarKey.ToString()}</avatarKey></item>"); } else { return($"<error>{MethodName}: parsekey avatarKey</error>"); } } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } }
} = "0.0.0.0"; // will be filled in later by Main() (gwyneth 20220425) /// <summary> /// Bootstrap method. /// </summary> /// <param name="args">Arguments passed to the application</param> /// <remarks>The arguments seem to get promptly ignored! (gwyneth 20220109)</remarks> static void Main(string[] args) { // LogManager.GetLogger(typeof(RestBot)); // new function to parse some useful arguments and do interesting things (gwyneth 20220425) ParseArguments(args); // see if we can get the version string try { // Note: we ought to also extract the Assembly name, we presume it's the default (gwyneth 20220426) #if Windows var fileVersionInfo = FileVersionInfo.GetVersionInfo("@RESTbot.dll"); #else var fileVersionInfo = FileVersionInfo.GetVersionInfo("@RESTbot"); #endif Version = fileVersionInfo.FileVersion + "-file"; } catch (Exception e1) { // nope, this doesn't work under macOS DebugUtilities.WriteDebug($"Cannot retrieve file version, exception caught: {e1.Message}"); // let's try to get the assembly name instead try { var assembly = Assembly.GetExecutingAssembly(); Version = assembly.GetName().Version + "-assembly"; } catch (Exception e2) { // nope, that didn't work either DebugUtilities.WriteDebug($"Cannot retrieve assembly version either, exception caught: {e2.Message}"); // finally, our last choice is trying the Informational Version try { var assembly = Assembly.GetEntryAssembly(); if (assembly != null) { var customAttribute = assembly.GetCustomAttribute <AssemblyInformationalVersionAttribute>(); if (customAttribute != null) { var infoVersion = customAttribute.InformationalVersion; if (infoVersion != null) { Version = infoVersion; } } } } catch (Exception e3) { // we're out of luck today, we cannot even get the Informational Versoin DebugUtilities.WriteDebug($"Also cannot retrieve informational version, exception caught: {e3.Message}"); // we'll have to stick with the hard-coded default Version instead... } } } if (Version == null) { Version = "0.0.0.0"; } DebugUtilities.WriteInfo($"RESTbot file version: {Version}"); DebugUtilities.WriteInfo($"Reading config file '{configFile}'..."); config = XMLConfig.Configuration.LoadConfiguration(configFile); if (config == null) { // configuration is mandatory! (gwyneth 20220213) DebugUtilities.WriteError($"Unable to open configuration file '{configFile}'! Aborting..."); Environment.Exit(1); return; } DebugUtilities.WriteInfo("RESTbot startup"); // Sessions should never be null (?) (gwyneth 20220214) if (Sessions == null) { // Trouble expects us later on, when adding and removing sessions... DebugUtilities.WriteError("Error initialising Sessions directory; it was set to null!"); } /// <summary>Get the file version of LibreMetaverse.</summary> /// <remarks><see href="https://stackoverflow.com/a/14612480/1035977"/> (gwyneth 20220414)</remarks> FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo(@"LibreMetaverse.dll"); // Print the file name and version number. DebugUtilities.WriteInfo($"LibreMetaverse DLL version: {myFileVersionInfo.FileDescription} {myFileVersionInfo.FileVersion}"); DebugUtilities.WriteInfo("Loading plugins"); RegisterAllCommands(Assembly.GetExecutingAssembly()); DebugUtilities.WriteDebug("Loading stateful plugins"); RegisterAllStatefulPlugins(Assembly.GetExecutingAssembly()); DebugUtilities.WriteInfo($"Listening on port {config.networking.port.ToString()}"); // Set up the listener / router Listener = new RESTBot.Server.Router(IPAddress.Parse(config.networking.ip), config.networking.port); StillRunning = true; DebugUtilities.WriteInfo("Startup complete"); uptime = DateTime.Now; // let's see if we can figure out how much memory is being wasted int stupidCounter = 0; while (StillRunning) { System.Threading.Thread.Sleep(1); //TODO: Replace above with a manualresetevent if (stupidCounter % 3600000 == 0) // stop every hour and check available memory (gwyneth 20220210) { CollectGarbage(); } stupidCounter++; } Listener.StillRunning = false; }
Process(RestBot b, Dictionary <string, string> Parameters) { // uint // regionX, // regionY; // Utils // .LongToUInts(b.Client.Network.CurrentSim.Handle, // out regionX, // out regionY); try { double x = 0.0f, y = 0.0f, z = 0.0f; bool check = true; if (Parameters.ContainsKey("x")) { check &= double.TryParse(Parameters["x"], out x); DebugUtilities.WriteDebug($"{MethodName} Parse: {Parameters["x"]} into {x}"); } else { check = false; } if (Parameters.ContainsKey("y")) { check &= double.TryParse(Parameters["y"], out y); } else { check = false; } if (Parameters.ContainsKey("z")) { check &= double.TryParse(Parameters["z"], out z); } else { check = false; } if (!check) { return($"<error>{MethodName} parameters have to be x, y, z</error>"); } goalPos = new Vector3((float)x, (float)y, (float)z); b.Client.Self.Movement.TurnToward(goalPos, false); return($"<{MethodName}><location>{b.Client.Self.GlobalPosition.X},{b.Client.Self.GlobalPosition.Y},{b.Client.Self.GlobalPosition.Z}</location><rotation>{b.Client.Self.SimRotation.X},{b.Client.Self.SimRotation.Y},{b.Client.Self.SimRotation.Z},{b.Client.Self.SimRotation.W}</rotation></{MethodName}>"); } catch (Exception e) { DebugUtilities.WriteError(e.Message); return($"<error>{MethodName}: {e.Message}</error>"); } } // end turnto process