new data store for chat, groups and avaters

This commit is contained in:
Madpeter 2022-05-29 17:38:28 +01:00
parent b9c1fb9511
commit a5851c5fe1
17 changed files with 1028 additions and 88 deletions

View file

@ -5,9 +5,9 @@ using SecondBotEvents.Services;
namespace SecondBotEvents.Commands
{
internal class Animation : CommandsAPI
internal class AnimationCommands : CommandsAPI
{
public Animation(EventsSecondBot setmaster) : base(setmaster)
public AnimationCommands(EventsSecondBot setmaster) : base(setmaster)
{
}

View file

@ -26,7 +26,7 @@ namespace SecondBotEvents.Commands
}
if (getClient().Network.CurrentSim == null)
{
return BasicReply("Error not in a sim", "nearmewithdetails");
return BasicReply("Error not in a sim");
}
List<NearMeDetails> BetterNearMe = new List<NearMeDetails>();
Dictionary<uint, Avatar> avcopy = getClient().Network.CurrentSim.ObjectsAvatars.Copy();
@ -44,7 +44,7 @@ namespace SecondBotEvents.Commands
BetterNearMe.Add(details);
}
}
return BasicReply(JsonConvert.SerializeObject(BetterNearMe), "nearmewithdetails");
return BasicReply(JsonConvert.SerializeObject(BetterNearMe));
}
[About("returns a list of all known avatars nearby")]
@ -59,7 +59,7 @@ namespace SecondBotEvents.Commands
}
if (getClient().Network.CurrentSim == null)
{
return BasicReply("Error not in a sim", "nearme");
return BasicReply("Error not in a sim");
}
Dictionary<UUID, string> NearMe = new Dictionary<UUID, string>();
Dictionary<uint, Avatar> avcopy = getClient().Network.CurrentSim.ObjectsAvatars.Copy();
@ -70,7 +70,44 @@ namespace SecondBotEvents.Commands
NearMe.Add(av.ID, av.Name);
}
}
return BasicReply(JsonConvert.SerializeObject(NearMe), "nearme");
return BasicReply(JsonConvert.SerializeObject(NearMe));
}
[About("searchs the AV database for the given UUID to find the name, if not found triggers a lookup")]
[ReturnHints("the name of the avatar")]
[ReturnHints("lookup")]
[ReturnHintsFailure("Not a vaild UUID")]
[Route(HttpVerbs.Get, "/Key2Name/{uuid}/{token}")]
public object Key2Name(string uuid, string token)
{
if (AllowToken(token) == false)
{
return Failure("Token not accepted");
}
UUID avUUID = UUID.Zero;
if (UUID.TryParse(uuid, out avUUID) == false)
{
return Failure("Not a vaild UUID");
}
return BasicReply(master.DataStoreService.getAvatarName(avUUID));
}
[About("searchs the AV database for the given name to find the UUID, if not found triggers a lookup")]
[ReturnHints("the uuid of the avatar")]
[ReturnHints("lookup")]
[ReturnHintsFailure("Not a vaild name")]
[Route(HttpVerbs.Get, "/Name2Key/{name}/{token}")]
public object Name2Key(string name, string token)
{
if (AllowToken(token) == false)
{
return Failure("Token not accepted");
}
if(name != null)
{
return Failure("Not a vaild name");
}
return BasicReply(master.DataStoreService.getAvatarUUID(name));
}
}
}

View file

@ -13,8 +13,9 @@ namespace SecondBotEvents.Commands
public Chat(EventsSecondBot setmaster) : base(setmaster)
{
}
[About("fetchs the last 20 localchat messages")]
[ReturnHints("array string")]
[About("fetchs the last localchat messages")]
[ReturnHints("json encoded array")]
[Route(HttpVerbs.Get, "/LocalChatHistory/{token}")]
public object LocalChatHistory(string token)
{
@ -22,7 +23,7 @@ namespace SecondBotEvents.Commands
{
return Failure("Token not accepted");
}
return Failure("@todo add local chat history");
return BasicReply(JsonConvert.SerializeObject(master.DataStoreService.getLocalChat()));
}
[About("sends a message to localchat")]
@ -51,11 +52,7 @@ namespace SecondBotEvents.Commands
return Failure("Invaild channel", "Say", new [] { channel, message });
}
getClient().Self.Chat(message, channelnum, ChatType.Normal);
if (channelnum == 0)
{
// @todo add message to localchat
}
return Failure("@todo get localchat history");
return BasicReply("ok");
}
@ -85,7 +82,7 @@ namespace SecondBotEvents.Commands
return BasicReply("ok", "IM", new [] { avatar, message });
}
[About("gets a full list of all chat windows")]
[About("gets a full list of all avatar chat windows")]
[ReturnHints("array UUID = Name")]
[Route(HttpVerbs.Get, "/chatwindows/{token}")]
public object chatwindows(string token)
@ -94,10 +91,10 @@ namespace SecondBotEvents.Commands
{
return Failure("Token not accepted");
}
return Failure("@todo chat windows list");
return BasicReply(JsonConvert.SerializeObject(master.DataStoreService.getAvatarImWindows()));
}
[About("gets a list of chat windows with unread messages")]
[About("gets a list of chat windows from avatars with unread messages")]
[ReturnHints("array of UUID")]
[Route(HttpVerbs.Get, "/listwithunread/{token}")]
public object listwithunread(string token)
@ -106,11 +103,10 @@ namespace SecondBotEvents.Commands
{
return Failure("Token not accepted");
}
List<UUID> unreadimwindows = new List<UUID>();
return Failure("@todo chat windows list unread");
return BasicReply(JsonConvert.SerializeObject(master.DataStoreService.getAvatarImWindowsUnread()));
}
[About("gets if there are any unread im messages at all")]
[About("gets if there are any unread im messages from avatars at all")]
[ReturnHints("True|False")]
[Route(HttpVerbs.Get, "/haveunreadims/{token}")]
public object haveunreadims(string token)
@ -119,28 +115,27 @@ namespace SecondBotEvents.Commands
{
return Failure("Token not accepted");
}
return Failure("@todo have any unread ims");
return BasicReply(master.DataStoreService.getAvatarImWindowsUnreadAny().ToString());
}
[About("gets the chat from the selected window")]
[ArgHints("window", "URLARG", "the UUID of the chat window")]
[ReturnHintsFailure("Window UUID invaild")]
[About("gets the chat from the selected window for avatar")]
[ArgHints("window", "URLARG", "the UUID of the avatar you wish to view the chat from")]
[ReturnHintsFailure("avatar UUID invaild")]
[ReturnHints("Array of text")]
[Route(HttpVerbs.Get, "/getimchat/{window}/{token}")]
public object getimchat(string window, string token)
[Route(HttpVerbs.Get, "/getimchat/{avatarid}/{token}")]
public object getimchat(string avatarid, string token)
{
if (AllowToken(token) == false)
{
return Failure("Token not accepted");
}
if (UUID.TryParse(window, out UUID windowUUID) == false)
UUID avatarUUID = UUID.Zero;
if (UUID.TryParse(avatarid, out avatarUUID) == false)
{
return Failure("Window UUID invaild", "getimchat", new[] { window });
return Failure("avatar UUID invaild");
}
return Failure("@todo chat window fetch");
return BasicReply(JsonConvert.SerializeObject(master.DataStoreService.getAvatarImWindow(avatarUUID)));
}
}
}

View file

@ -9,9 +9,9 @@ using System.Threading;
namespace SecondBotEvents.Commands
{
public class Group : CommandsAPI
public class GroupCommands : CommandsAPI
{
public Group(EventsSecondBot setmaster) : base(setmaster)
public GroupCommands(EventsSecondBot setmaster) : base(setmaster)
{
}

View file

@ -12,9 +12,9 @@ using System.Linq;
namespace SecondBotEvents.Commands
{
public class Inventory : CommandsAPI
public class InventoryCommands : CommandsAPI
{
public Inventory(EventsSecondBot setmaster) : base(setmaster)
public InventoryCommands(EventsSecondBot setmaster) : base(setmaster)
{
}

View file

@ -10,31 +10,31 @@ namespace SecondBotEvents.Config
protected override void MakeSettings()
{
filename = "basic";
settings.Add("username");
settings.Add("password");
settings.Add("loginURI");
settings.Add("Username");
settings.Add("Password");
settings.Add("LoginURI");
}
public string GetUsername()
{
return ReadSettingAsString("username","Firstname Lastname");
return ReadSettingAsString("Username","Firstname Lastname");
}
public string GetPassword()
{
return ReadSettingAsString("password", "passwordHere");
return ReadSettingAsString("Password", "passwordHere");
}
public string GetFirstName()
{
string[] bits = ReadSettingAsString("username", "Firstname Lastname").Split(" ");
string[] bits = GetUsername().Split(" ");
return bits[0];
}
public string GetLastName()
{
string[] bits = ReadSettingAsString("username", "Firstname Lastname").Split(" ");
string[] bits = GetUsername().Split(" ");
if(bits.Length == 2)
{
return bits[1];
@ -44,7 +44,7 @@ namespace SecondBotEvents.Config
public string GetLoginURI()
{
return ReadSettingAsString("loginURI", "secondlife");
return ReadSettingAsString("LoginURI", "secondlife");
}
}
}

View file

@ -11,48 +11,55 @@ namespace SecondBotEvents.Config
protected override void MakeSettings()
{
filename = "commands";
settings.Add("allowFundsCommands");
settings.Add("allowIMcontrol");
settings.Add("sharedSecret");
settings.Add("onlySelectedAvs");
settings.Add("avsCSV");
settings.Add("enforceTimeWindow");
settings.Add("timeWindowSecs");
settings.Add("AllowFundsCommands");
settings.Add("AllowIMcontrol");
settings.Add("SharedSecret");
settings.Add("OnlyMasterAvs");
settings.Add("MastersCSV");
settings.Add("EnforceTimeWindow");
settings.Add("TimeWindowSecs");
settings.Add("Enabled");
}
public bool GetEnabled()
{
return ReadSettingAsBool("Enabled", true);
}
public string[] GetMastersCSV()
{
return ReadSettingAsString("MastersCSV", "Madpeter Zond").Split(",");
}
public bool GetEnforceTimeWindow()
{
return ReadSettingAsBool("enforceTimeWindow", false);
return ReadSettingAsBool("EnforceTimeWindow", false);
}
public int GetTimeWindowSecs()
{
return ReadSettingAsInt("timeWindowSecs", 35);
return ReadSettingAsInt("TimeWindowSecs", 35);
}
public bool GetOnlySelectedAvs()
public bool GetOnlyMasterAvs()
{
return ReadSettingAsBool("onlySelectedAvs", false);
return ReadSettingAsBool("OnlyMasterAvs", false);
}
public List<string> GetAvsCSV()
{
return ReadSettingAsString("avsCSV", "Madpeter Zond,Madpeter Zond,Madpeter Zond").Split(",").ToList();
}
public bool GetAllowFundsCommands()
{
return ReadSettingAsBool("allowFundsCommands", false);
return ReadSettingAsBool("AllowFundsCommands", false);
}
public bool GetAllowIMcontrol()
{
return ReadSettingAsBool("allowIMcontrol", false);
return ReadSettingAsBool("AllowIMcontrol", false);
}
public string GetSharedSecret()
{
return ReadSettingAsString("sharedSecret", "ThisIsMySecret");
return ReadSettingAsString("SharedSecret", "ThisIsMySecret");
}
}
}

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SecondBotEvents.Config
{
public class DataStoreConfig : Config
{
public DataStoreConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
protected override void MakeSettings()
{
filename = "datastore";
settings.Add("AvatarsPurgeAfterMins");
settings.Add("LocalChatHistoryLimit");
settings.Add("GroupChatHistoryLimitPerGroup");
settings.Add("ImChatHistoryLimit");
settings.Add("NotecardStoragePurgeAfterMins");
}
public int GetAvatarsPurgeAfterMins()
{
return ReadSettingAsInt("AvatarsPurgeAfterMins", 25);
}
public int GetLocalChatHistoryLimit()
{
return ReadSettingAsInt("LocalChatHistoryLimit", 150);
}
public int GetGroupChatHistoryLimitPerGroup()
{
return ReadSettingAsInt("GroupChatHistoryLimitPerGroup", 50);
}
public int GetImChatHistoryLimit()
{
return ReadSettingAsInt("ImChatHistoryLimit", 50);
}
public int GetNotecardStoragePurgeAfterMins()
{
return ReadSettingAsInt("NotecardStoragePurgeAfterMins", 10);
}
}
}

View file

@ -10,30 +10,30 @@ namespace SecondBotEvents.Config
protected override void MakeSettings()
{
filename = "discord";
settings.Add("enabled");
settings.Add("serverID");
settings.Add("clientToken");
settings.Add("allowDiscordCommands");
settings.Add("Enabled");
settings.Add("ServerID");
settings.Add("ClientToken");
settings.Add("AllowDiscordCommands");
}
public bool GetEnabled()
{
return ReadSettingAsBool("enabled");
return ReadSettingAsBool("Enabled");
}
public ulong GetServerID()
{
return ReadSettingAsUlong("serverID", 0);
return ReadSettingAsUlong("ServerID", 0);
}
public string GetClientToken()
{
return ReadSettingAsString("clientToken","tokenPlzKThanks");
return ReadSettingAsString("ClientToken","tokenPlzKThanks");
}
public bool GetAllowDiscordCommands()
{
return ReadSettingAsBool("allowDiscordCommands");
return ReadSettingAsBool("AllowDiscordCommands");
}
}
}

View file

@ -10,18 +10,18 @@ namespace SecondBotEvents.Config
protected override void MakeSettings()
{
filename = "http";
settings.Add("enabled");
settings.Add("host");
settings.Add("Enabled");
settings.Add("Host");
}
public string GetHost()
{
return ReadSettingAsString("host", "http://*:80");
return ReadSettingAsString("Host", "http://*:80");
}
public bool GetEnabled()
{
return ReadSettingAsBool("enabled", false);
return ReadSettingAsBool("Enabled", false);
}
}

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SecondBotEvents.Config
{
public class InteractionConfig : Config
{
public InteractionConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
protected override void MakeSettings()
{
filename = "interaction";
settings.Add("AcceptTeleports");
settings.Add("AcceptGroupInvites");
settings.Add("AcceptInventory");
settings.Add("EnableJsonOutputEvents");
settings.Add("JsonOutputEventsTarget");
settings.Add("AcceptFriendRequests");
settings.Add("Enabled");
}
public bool GetEnabled()
{
return ReadSettingAsBool("Enabled", true);
}
public bool GetAcceptTeleports()
{
return ReadSettingAsBool("AcceptTeleports", true);
}
public bool GetAcceptGroupInvites()
{
return ReadSettingAsBool("AcceptGroupInvites", true);
}
public bool GetAcceptInventory()
{
return ReadSettingAsBool("AcceptInventory", true);
}
public string GetJsonOutputEventsTarget()
{
return ReadSettingAsString("JsonOutputEventsTarget", "none");
}
public bool GetEnableJsonOutputEvents()
{
return ReadSettingAsBool("EnableJsonOutputEvents", true);
}
public bool GetAcceptFriendRequests()
{
return ReadSettingAsBool("AcceptFriendRequests", true);
}
}
}

View file

@ -26,6 +26,8 @@ namespace SecondBotEvents
public CommandsService CommandsService = null;
public BotClientService botClient = null;
public DiscordService DiscordService = null;
public InteractionService InteractionService = null;
public DataStoreService DataStoreService = null;
private EventHandler<BotClientNotice> botclient_eventNotices;
@ -72,10 +74,13 @@ namespace SecondBotEvents
}
protected void StartServices()
{
DataStoreService = new DataStoreService(this);
DataStoreService.Start();
botClient = new BotClientService(this);
HttpService = new HttpService(this);
CommandsService = new CommandsService(this);
DiscordService = new DiscordService(this);
InteractionService = new InteractionService(this);
if (botClient.IsLoaded() == false)
{
Console.WriteLine("Config is not loaded :(");
@ -85,10 +90,15 @@ namespace SecondBotEvents
HttpService.Start();
CommandsService.Start();
DiscordService.Start();
InteractionService.Start();
botClient.Start();
}
protected void StopServices()
{
if(DataStoreService != null)
{
DataStoreService.Stop();
}
if (HttpService != null)
{
HttpService.Stop();
@ -101,6 +111,10 @@ namespace SecondBotEvents
{
DiscordService.Stop();
}
if(InteractionService != null)
{
InteractionService.Stop();
}
if(botClient != null)
{
botClient.Stop();

View file

@ -18,6 +18,10 @@ namespace SecondBotEvents.Services
public CommandsService(EventsSecondBot setMaster) : base(setMaster)
{
myConfig = new CommandsConfig(master.fromEnv, master.fromFolder);
if (myConfig.GetEnabled() == false)
{
return;
}
loadCommands();
}
@ -147,12 +151,12 @@ namespace SecondBotEvents.Services
{
break;
}
if (myConfig.GetOnlySelectedAvs() == false)
if (myConfig.GetOnlyMasterAvs() == false)
{
break;
}
requireSigning = false;
acceptMessage = myConfig.GetAvsCSV().Contains(e.IM.FromAgentName);
acceptMessage = myConfig.GetMastersCSV().Contains(e.IM.FromAgentName);
break;
}
default:
@ -181,7 +185,7 @@ namespace SecondBotEvents.Services
}
}
protected void SmartCommandReply(bool run_status, string target, string output, string command)
public void SmartCommandReply(bool run_status, string target, string output, string command)
{
string mode = "CHAT";
UUID target_avatar = UUID.Zero;
@ -194,12 +198,9 @@ namespace SecondBotEvents.Services
{
mode = "IM";
}
else
else if (int.TryParse(target, out target_channel) == false)
{
if (int.TryParse(target, out target_channel) == false)
{
mode = "None";
}
return;
}
if (mode == "CHAT")
{
@ -318,6 +319,13 @@ namespace SecondBotEvents.Services
{
master.botClient.client.Network.SimConnected -= BotLoggedIn;
master.botClient.client.Self.IM += BotImMessage;
if (myConfig.GetOnlyMasterAvs() == true)
{
foreach(string A in myConfig.GetMastersCSV())
{
master.DataStoreService.knownAvatar(A);
}
}
acceptNewCommands = true;
Console.WriteLine("Commands service [accepting IM commands]");
}

View file

@ -0,0 +1,532 @@
using Newtonsoft.Json;
using OpenMetaverse;
using SecondBotEvents.Config;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace SecondBotEvents.Services
{
public class DataStoreService : Services
{
protected DataStoreConfig myConfig;
protected bool botConnected = false;
public DataStoreService(EventsSecondBot setMaster) : base(setMaster)
{
myConfig = new DataStoreConfig(master.fromEnv,master.fromFolder);
}
public override string Status()
{
if(myConfig == null)
{
return "DataStore Service [Config status broken]";
}
return "DataStore Service [Waiting for bot]";
}
protected Dictionary<string, UUID> avatarsName2Key = new Dictionary<string, UUID>();
protected Dictionary<UUID, string> avatarsKey2Name = new Dictionary<UUID, string>();
protected Dictionary<UUID, string> groupsKey2Name = new Dictionary<UUID, string>();
protected Dictionary<string, UUID> groupsName2Key = new Dictionary<string, UUID>();
protected List<string> localChatHistory = new List<string>();
protected Dictionary<UUID, List<string>> chatWindows = new Dictionary<UUID, List<string>>(); // sess, list chat
protected Dictionary<UUID, bool> chatWindowsUnread = new Dictionary<UUID, bool>(); // sess, bool
protected Dictionary<UUID, bool> chatWindowsIsGroup = new Dictionary<UUID, bool>(); // sess, bool
protected Dictionary<UUID, UUID> chatWindowsOwner = new Dictionary<UUID, UUID>(); // sess, owner id
protected void recordChat(UUID sessionID, string fromname, string message)
{
if(chatWindows.ContainsKey(sessionID) == false)
{
return; // chat window not open unable to record message
}
chatWindows[sessionID].Add(fromname + ": " + message);
chatWindowsUnread[sessionID] = true;
int purgeLen = myConfig.GetImChatHistoryLimit();
if(chatWindowsIsGroup[sessionID] == true)
{
purgeLen = myConfig.GetGroupChatHistoryLimitPerGroup();
}
int counter = chatWindows[sessionID].Count();
while (counter > purgeLen)
{
chatWindows[sessionID].RemoveAt(0);
counter--;
}
}
protected void openChatWindow(bool group, UUID sessionID, UUID ownerID)
{
if (chatWindows.ContainsKey(sessionID) == true)
{
return; // window already open
}
if (chatWindowsOwner.ContainsValue(ownerID) == true)
{
// new session find the old one and update to this session
Dictionary<UUID, UUID> oldOwners = chatWindowsOwner;
foreach (KeyValuePair<UUID,UUID> kvp in oldOwners)
{
if(kvp.Value == ownerID)
{
chatWindows.Add(sessionID, chatWindows[kvp.Key]);
chatWindowsUnread.Add(sessionID, chatWindowsUnread[kvp.Key]);
chatWindowsIsGroup.Add(sessionID, chatWindowsIsGroup[kvp.Key]);
chatWindowsOwner.Add(sessionID, ownerID);
chatWindows.Remove(kvp.Key);
chatWindowsUnread.Remove(kvp.Key);
chatWindowsIsGroup.Remove(kvp.Key);
chatWindowsOwner.Remove(kvp.Key);
break;
}
}
return; // chat recovered
}
chatWindows.Add(sessionID, new List<string>());
chatWindowsUnread.Add(sessionID, false);
chatWindowsIsGroup.Add(sessionID, group);
chatWindowsOwner.Add(sessionID, ownerID);
// new window now open
}
protected Dictionary<string, KeyValuePair<string, long>> notecardDataStore = new Dictionary<string, KeyValuePair<string, long>>();
public List<string> getLocalChat()
{
return localChatHistory;
}
public bool knownGroup(UUID groupId, string name)
{
if(groupId != UUID.Zero)
{
return groupsKey2Name.ContainsKey(groupId);
}
return groupsName2Key.ContainsKey(name);
}
public List<string> getGroupChat(UUID groupId)
{
if(chatWindows.ContainsKey(groupId) == false)
{
return new List<string>();
}
return chatWindows[groupId];
}
/*
* checks if the avatar is known to the system
* if not the bot will attempt to
* lookup the avatar
*/
public bool knownAvatar(string name)
{
return knownAvatar(name, UUID.Zero);
}
protected int ObjectsAvatarsHash = 0;
/*
* checks if the avatar is known to the system
* if not the bot will attempt to
* lookup the avatar
*/
public bool knownAvatar(string name, UUID avatarId)
{
int currentHash = master.botClient.client.Network.CurrentSim.ObjectsAvatars.GetHashCode();
if (currentHash != ObjectsAvatarsHash)
{
ObjectsAvatarsHash = currentHash;
List<Avatar> avs = master.botClient.client.Network.CurrentSim.ObjectsAvatars.Copy().Values.ToList();
foreach (Avatar A in avs)
{
addAvatar(A.ID, A.Name);
}
}
if (avatarId != UUID.Zero)
{
if(avatarsKey2Name.ContainsKey(avatarId) == false)
{
master.botClient.client.Avatars.RequestAvatarName(avatarId);
return false;
}
return true;
}
string[] bits = name.Split(' ');
if (bits.Length == 1)
{
name = name + " Resident";
}
if (avatarsName2Key.ContainsKey(name) == false)
{
master.botClient.client.Avatars.RequestAvatarNameSearch(name, UUID.Random());
return false;
}
return true;
}
public string getAvatarName(UUID avatarId)
{
if(knownAvatar(null, avatarId) == false)
{
return "lookup";
}
return avatarsKey2Name[avatarId];
}
public string getAvatarUUID(string name)
{
if (knownAvatar(name) == false)
{
return "lookup";
}
return avatarsName2Key[name].ToString();
}
public List<string> getAvatarIms(UUID avatarId)
{
if(chatWindowsOwner.ContainsValue(avatarId) == false)
{
return new List<string>();
}
UUID sessionID = UUID.Zero;
foreach(KeyValuePair<UUID, UUID> links in chatWindowsOwner)
{
if(links.Value == avatarId)
{
sessionID = links.Key;
break;
}
}
if (sessionID == UUID.Zero)
{
return new List<string>();
}
return chatWindows[sessionID];
}
public Dictionary<UUID,string> getAvatarImWindows()
{
Dictionary<UUID, string> windows = new Dictionary<UUID, string>();
foreach (KeyValuePair<UUID,bool> links in chatWindowsIsGroup)
{
if(links.Value == true)
{
continue;
}
if (chatWindowsIsGroup[links.Key] == true)
{
continue;
}
windows[chatWindowsOwner[links.Key]] = getAvatarName(chatWindowsOwner[links.Key]);
}
return windows;
}
public List<string> getAvatarImWindow(UUID avatarId)
{
if (chatWindowsOwner.ContainsValue(avatarId) == false)
{
return new List<string>();
}
UUID sessionID = UUID.Zero;
foreach (KeyValuePair<UUID, UUID> links in chatWindowsOwner)
{
if (links.Value == avatarId)
{
sessionID = links.Key;
break;
}
}
if(chatWindowsIsGroup[sessionID] == true)
{
return new List<string>();
}
return chatWindows[sessionID];
}
public bool getAvatarImWindowsUnreadAny()
{
bool replyState = false;
foreach (KeyValuePair<UUID, bool> links in chatWindowsIsGroup)
{
if (links.Value == true)
{
continue;
}
if (chatWindowsIsGroup[links.Key] == true)
{
continue;
}
if (chatWindowsUnread[links.Key] == false)
{
continue;
}
replyState = true;
break;
}
return replyState;
}
public List<UUID> getAvatarImWindowsUnread()
{
List<UUID> windows = new List<UUID>();
foreach (KeyValuePair<UUID, bool> links in chatWindowsIsGroup)
{
if (links.Value == true)
{
continue;
}
if (chatWindowsIsGroup[links.Key] == true)
{
continue;
}
if (chatWindowsUnread[links.Key] == false)
{
continue;
}
windows.Add(chatWindowsOwner[links.Key]);
}
return windows;
}
protected void reset()
{
avatarsName2Key = new Dictionary<string, UUID>();
avatarsKey2Name = new Dictionary<UUID, string>();
groupsKey2Name = new Dictionary<UUID, string>();
groupsName2Key = new Dictionary<string, UUID>();
localChatHistory = new List<string>();
notecardDataStore = new Dictionary<string, KeyValuePair<string, long>>();
}
protected void BotClientRestart(object o, BotClientNotice e)
{
botConnected = false;
Console.WriteLine("DataStore Service [Attached to new client]");
master.botClient.client.Network.LoggedOut += BotLoggedOut;
master.botClient.client.Network.SimConnected += BotLoggedIn;
}
protected void BotLoggedOut(object o, LoggedOutEventArgs e)
{
botConnected = false;
master.botClient.client.Network.SimConnected += BotLoggedIn;
Console.WriteLine("DataStore Service [Standby]");
}
protected void BotLoggedIn(object o, SimConnectedEventArgs e)
{
master.botClient.client.Network.SimConnected -= BotLoggedIn;
master.botClient.client.Groups.CurrentGroups += GroupCurrent;
master.botClient.client.Groups.GroupJoinedReply += GroupJoin;
master.botClient.client.Groups.GroupLeaveReply += GroupLeave;
master.botClient.client.Avatars.UUIDNameReply += AvatarUUIDName;
master.botClient.client.Friends.FriendNames += AvatarFriendNames;
master.botClient.client.Self.IM += ImEvent;
master.botClient.client.Self.ChatFromSimulator += ChatFromSim;
master.botClient.client.Groups.RequestCurrentGroups();
botConnected = true;
Console.WriteLine("DataStore Service [Active]");
}
readonly string[] hard_blocked_agents = new[] { "secondlife", "second life" };
protected void ChatFromSim(object o, ChatEventArgs e)
{
switch(e.Type)
{
case ChatType.Whisper:
case ChatType.Normal:
case ChatType.Shout:
{
if (e.SourceType != ChatSourceType.Agent)
{
break;
}
if (hard_blocked_agents.Contains(e.FromName.ToLowerInvariant()) == true)
{
break;
}
localChatHistory.Add(e.FromName + ": " + e.Message);
cleanLocalChat();
break;
}
default:
{
break;
}
}
}
protected void GroupCurrent(object o, CurrentGroupsEventArgs e)
{
List<UUID> missingGroups = groupsKey2Name.Keys.ToList();
foreach(Group G in e.Groups.Values)
{
missingGroups.Remove(G.ID);
if (groupsKey2Name.ContainsKey(G.ID) == true)
{
continue;
}
groupsKey2Name.Add(G.ID, G.Name);
groupsName2Key.Add(G.Name, G.ID);
master.botClient.client.Self.RequestJoinGroupChat(G.ID);
}
foreach(UUID G in missingGroups)
{
if (groupsKey2Name.ContainsKey(G) == false)
{
continue;
}
string name = groupsKey2Name[G];
groupsKey2Name.Remove(G);
groupsName2Key.Remove(name);
// @todo remove group chat here
}
}
protected void GroupJoin(object o, GroupOperationEventArgs e)
{
master.botClient.client.Groups.RequestCurrentGroups();
}
protected void GroupLeave(object o, GroupOperationEventArgs e)
{
master.botClient.client.Groups.RequestCurrentGroups();
}
protected void addAvatar(UUID id, string name)
{
if (avatarsKey2Name.ContainsKey(id) == true)
{
return;
}
avatarsKey2Name.Add(id, name);
avatarsName2Key.Add(name, id);
}
protected void AvatarUUIDName(object o, UUIDNameReplyEventArgs e)
{
foreach(KeyValuePair<UUID,string> pair in e.Names)
{
addAvatar(pair.Key, pair.Value);
}
}
protected void AvatarFriendNames(object o, FriendNamesEventArgs e)
{
foreach (KeyValuePair<UUID, string> pair in e.Names)
{
addAvatar(pair.Key, pair.Value);
}
}
protected void cleanGroupChat(UUID groupID)
{
if(groupsKey2Name.ContainsKey(groupID) == false)
{
return;
}
// @todo clean group chat here
}
protected void cleanAvatarChat(UUID avatarID)
{
if (avatarsKey2Name.ContainsKey(avatarID) == false)
{
return;
}
// @todo clean avatar chat here
}
protected void cleanLocalChat()
{
if (localChatHistory.Count <= myConfig.GetLocalChatHistoryLimit())
{
return;
}
localChatHistory.RemoveAt(0);
}
protected void ImEvent(object o, InstantMessageEventArgs e)
{
switch(e.IM.Dialog)
{
case InstantMessageDialog.GroupNotice:
{
if(groupsKey2Name.ContainsKey(e.IM.IMSessionID) == false)
{
break;
}
openChatWindow(true, e.IM.IMSessionID, e.IM.IMSessionID);
recordChat(e.IM.IMSessionID, "**Notice**", e.IM.Message);
break;
}
case InstantMessageDialog.SessionSend:
case InstantMessageDialog.MessageFromAgent:
{
if (groupsKey2Name.ContainsKey(e.IM.IMSessionID) == true)
{
// group IM
openChatWindow(true, e.IM.IMSessionID, e.IM.IMSessionID);
recordChat(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.Message);
break;
}
addAvatar(e.IM.FromAgentID, e.IM.FromAgentName);
openChatWindow(false, e.IM.IMSessionID, e.IM.FromAgentID);
recordChat(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.Message);
break;
}
default:
{
break;
}
}
}
public override void Start()
{
Stop();
master.BotClientNoticeEvent += BotClientRestart;
Console.WriteLine("DataStore Service [Starting]");
}
public override void Stop()
{
reset();
master.BotClientNoticeEvent -= BotClientRestart;
if (master.botClient != null)
{
if (master.botClient.client != null)
{
master.botClient.client.Groups.CurrentGroups -= GroupCurrent;
master.botClient.client.Groups.GroupJoinedReply -= GroupJoin;
master.botClient.client.Groups.GroupLeaveReply -= GroupLeave;
master.botClient.client.Avatars.UUIDNameReply -= AvatarUUIDName;
master.botClient.client.Friends.FriendNames -= AvatarFriendNames;
master.botClient.client.Self.IM -= ImEvent;
master.botClient.client.Self.ChatFromSimulator -= ChatFromSim;
}
}
Console.WriteLine("DataStore Service [Stopping]");
}
}
}

View file

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using SecondBotEvents.Commands;
namespace SecondBotEvents.Services
{
@ -105,6 +106,23 @@ namespace SecondBotEvents.Services
.WithUrlPrefix(myConfig.GetHost())
.WithMode(HttpListenerMode.EmbedIO))
.WithCors()
.WithWebApi("/animation", m => m.WithController(() => new AnimationCommands(master)))
.WithWebApi("/avatars", m => m.WithController(() => new Avatars(master)))
.WithWebApi("/chat", m => m.WithController(() => new Chat(master)))
.WithWebApi("/core", m => m.WithController(() => new Core(master)))
.WithWebApi("/dialogs", m => m.WithController(() => new Dialogs(master)))
.WithWebApi("/discord", m => m.WithController(() => new DiscordCommands(master)))
.WithWebApi("/estate", m => m.WithController(() => new Estate(master)))
.WithWebApi("/friends", m => m.WithController(() => new Friends(master)))
.WithWebApi("/funds", m => m.WithController(() => new Funds(master)))
.WithWebApi("/group", m => m.WithController(() => new GroupCommands(master)))
.WithWebApi("/info", m => m.WithController(() => new Info(master)))
.WithWebApi("/inventory", m => m.WithController(() => new InventoryCommands(master)))
.WithWebApi("/movement", m => m.WithController(() => new Movement(master)))
.WithWebApi("/notecard", m => m.WithController(() => new Notecard(master)))
.WithWebApi("/parcel", m => m.WithController(() => new ParcelCommands(master)))
.WithWebApi("/self", m => m.WithController(() => new Self(master)))
.WithWebApi("/streamadmin", m => m.WithController(() => new StreamAdmin(master)))
.WithModule(new ActionModule("/", HttpVerbs.Any, ctx => ctx.SendDataAsync(new { Message = "Error" }))
);
HTTPendpoint.RunAsync();
@ -180,11 +198,12 @@ namespace SecondBotEvents.Services
protected void ProcessAvatar(string avatar)
{
string[] bits = avatar.Split(' ');
avataruuid = UUID.Zero;
// @todo Avatar storage system
UUID.TryParse(avatar, out avataruuid);
string UUIDfetch = master.DataStoreService.getAvatarUUID(avatar);
if(UUIDfetch != "lookup")
{
UUID.TryParse(UUIDfetch, out avataruuid);
}
}
protected void SuccessNoReturn(string command)

View file

@ -0,0 +1,225 @@
using Newtonsoft.Json;
using OpenMetaverse;
using SecondBotEvents.Config;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace SecondBotEvents.Services
{
public class InteractionService : Services
{
protected InteractionConfig myConfig;
protected bool botConnected = false;
public InteractionService(EventsSecondBot setMaster) : base(setMaster)
{
myConfig = new InteractionConfig(master.fromEnv,master.fromFolder);
if (myConfig.GetEnabled() == false)
{
Console.WriteLine(Status());
return;
}
}
public override string Status()
{
if(myConfig == null)
{
return "Interaction Service [Config status broken]";
}
if (myConfig.GetEnabled() == false)
{
return "Interaction Service [- Not requested -]";
}
return "Interaction Service [Waiting for bot]";
}
protected bool processRequest(bool enabledByConfig, string avatarName, string sourceName)
{
if(myConfig.GetEnabled() == false)
{
jsonOuput(false, sourceName, avatarName, "Interactions hooks disabled");
return false;
}
if (enabledByConfig == false)
{
jsonOuput(false, sourceName, avatarName, "disabled by config");
return false;
}
if (master.CommandsService.myConfig.GetOnlyMasterAvs() == false)
{
// master avatars list is not enabled. unable to continue
jsonOuput(false, sourceName, avatarName, "masters AV list is disabled");
return false;
}
if (master.CommandsService.myConfig.GetMastersCSV().Contains(avatarName) == false)
{
jsonOuput(false, sourceName, avatarName, "Not from accepted master");
return false;
}
return true;
}
protected void BotClientRestart(object o, BotClientNotice e)
{
botConnected = false;
Console.WriteLine("Interaction Service [Attached to new client]");
master.botClient.client.Network.LoggedOut += BotLoggedOut;
master.botClient.client.Network.SimConnected += BotLoggedIn;
}
protected void jsonOuput(bool status, string eventype, string from, string why="", Dictionary<string, string> setMisc = null)
{
if (myConfig.GetEnableJsonOutputEvents() == false)
{
return;
}
master.CommandsService.SmartCommandReply(
status,
myConfig.GetJsonOutputEventsTarget(),
JsonConvert.SerializeObject(new interactionEvent(from, eventype, status, why, setMisc)),
"interactions"
);
}
protected void BotTeleportOffer(object o, InstantMessageEventArgs e)
{
if((e.IM.Dialog != InstantMessageDialog.RequestTeleport) && (e.IM.Dialog != InstantMessageDialog.RequestLure))
{
return;
}
string mode = "Teleport Bot to Av";
if (e.IM.Dialog == InstantMessageDialog.RequestLure)
{
mode = "Teleport Av to Bot";
}
if (processRequest(myConfig.GetAcceptTeleports(), e.IM.FromAgentName, mode) == false)
{
if (e.IM.Dialog != InstantMessageDialog.RequestLure)
{
return;
}
master.botClient.client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, false);
return;
}
jsonOuput(true, mode, e.IM.FromAgentName);
if (e.IM.Dialog == InstantMessageDialog.RequestLure)
{
master.botClient.client.Self.SendTeleportLure(e.IM.FromAgentID);
return;
}
master.botClient.client.Self.TeleportLureRespond(e.IM.FromAgentID, e.IM.IMSessionID, true);
}
protected void BotGroupInviteOffer(object o, GroupInvitationEventArgs e)
{
if (processRequest(myConfig.GetAcceptGroupInvites(), e.FromName, "GroupInvite") == false)
{
return;
}
e.Accept = true;
jsonOuput(true, "GroupInvite", e.FromName);
}
protected void BotInventoryOffer(object o, InventoryObjectOfferedEventArgs e)
{
if(processRequest(myConfig.GetAcceptInventory(),e.Offer.FromAgentName, "InventoryUpdate") == false)
{
return;
}
e.Accept = true;
Dictionary<string, string> details = new Dictionary<string, string>();
details.Add("itemuuid", e.ObjectID.ToString());
details.Add("itemtype", e.AssetType.ToString());
InventoryItem itm = master.botClient.client.Inventory.FetchItem(e.ObjectID, master.botClient.client.Self.AgentID, 2000);
if(itm != null)
{
details.Add("itemname", itm.Name);
}
jsonOuput(true, "InventoryUpdate", e.Offer.FromAgentName, "see misc", details);
}
protected void BotFriendRequested(object o, FriendshipOfferedEventArgs e)
{
if (processRequest(myConfig.GetAcceptGroupInvites(), e.AgentName, "FriendRequest") == false)
{
return;
}
master.botClient.client.Friends.AcceptFriendship(e.AgentID, e.SessionID);
jsonOuput(true, "FriendRequest", e.AgentName);
}
protected void BotLoggedOut(object o, LoggedOutEventArgs e)
{
botConnected = false;
master.botClient.client.Network.SimConnected += BotLoggedIn;
Console.WriteLine("Interaction Service [Standby]");
}
protected void BotLoggedIn(object o, SimConnectedEventArgs e)
{
master.botClient.client.Network.SimConnected -= BotLoggedIn;
master.botClient.client.Friends.FriendshipOffered += BotFriendRequested;
master.botClient.client.Inventory.InventoryObjectOffered += BotInventoryOffer;
master.botClient.client.Groups.GroupInvitation += BotGroupInviteOffer;
master.botClient.client.Self.IM += BotTeleportOffer;
botConnected = true;
Console.WriteLine("Interaction Service [Active]");
}
public override void Start()
{
if (myConfig.GetEnabled() == false)
{
Console.WriteLine("Interaction Service [- Not requested -]");
return;
}
Stop();
master.BotClientNoticeEvent += BotClientRestart;
Console.WriteLine("Interaction Service [Starting]");
}
public override void Stop()
{
if (myConfig.GetEnabled() == false)
{
return;
}
master.BotClientNoticeEvent -= BotClientRestart;
if (master.botClient != null)
{
if (master.botClient.client != null)
{
master.botClient.client.Friends.FriendshipOffered -= BotFriendRequested;
master.botClient.client.Inventory.InventoryObjectOffered -= BotInventoryOffer;
master.botClient.client.Groups.GroupInvitation -= BotGroupInviteOffer;
}
}
Console.WriteLine("Interaction Service [Stopping]");
}
}
public class interactionEvent
{
public string from = "?";
public string eventType = "?";
public bool accepted = false;
public string info = "";
public Dictionary<string, string> misc = new Dictionary<string, string>();
public interactionEvent(string setFrom, string setType, bool setAccepted, string setInfo, Dictionary<string, string> setMisc = null)
{
accepted = setAccepted;
from = setFrom;
eventType = setType;
info = setInfo;
if(setMisc != null)
{
misc = setMisc;
}
}
}
}

View file

@ -10,7 +10,7 @@ namespace SecondBotEvents
public static Dictionary<string, Type> getCommandModules()
{
Dictionary<string, Type> reply = new Dictionary<string, Type>();
reply.Add("animation", typeof(Animation));
reply.Add("animation", typeof(AnimationCommands));
reply.Add("avatars", typeof(Avatars));
reply.Add("chat", typeof(Chat));
reply.Add("core", typeof(Core));
@ -19,9 +19,9 @@ namespace SecondBotEvents
reply.Add("estate", typeof(Estate));
reply.Add("friends", typeof(Friends));
reply.Add("funds", typeof(Funds));
reply.Add("group", typeof(Group));
reply.Add("group", typeof(GroupCommands));
reply.Add("info", typeof(Info));
reply.Add("inventory", typeof(Inventory));
reply.Add("inventory", typeof(InventoryCommands));
reply.Add("movement", typeof(Movement));
reply.Add("notecard", typeof(Notecard));
reply.Add("parcel", typeof(ParcelCommands));