getting closer to doing something.

This commit is contained in:
Madpeter 2022-03-18 02:57:06 +00:00
parent 50ff8f0c5f
commit 41bf241970
23 changed files with 1081 additions and 509 deletions

View file

@ -1,9 +1,10 @@
using EmbedIO; using EmbedIO;
using EmbedIO.Routing; using EmbedIO.Routing;
using EmbedIO.WebApi; using EmbedIO.WebApi;
using Newtonsoft.Json;
using OpenMetaverse; using OpenMetaverse;
using SecondBotEvents.Services; using SecondBotEvents.Services;
using System.Collections.Generic;
namespace SecondBotEvents.Commands namespace SecondBotEvents.Commands
{ {
@ -21,7 +22,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
return BasicReply(JsonConvert.SerializeObject(bot.getLocalChatHistory()), "LocalChatHistory"); return Failure("@todo add local chat history");
} }
[About("sends a message to localchat")] [About("sends a message to localchat")]
@ -52,9 +53,9 @@ namespace SecondBotEvents.Commands
getClient().Self.Chat(message, channelnum, ChatType.Normal); getClient().Self.Chat(message, channelnum, ChatType.Normal);
if (channelnum == 0) if (channelnum == 0)
{ {
bot.AddToLocalChat(getClient().Self.Name, message); // @todo add message to localchat
} }
return BasicReply(JsonConvert.SerializeObject(bot.getLocalChatHistory()), "Say", new [] { channel, message }); return Failure("@todo get localchat history");
} }
@ -80,7 +81,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Message empty", "IM", new [] { avatar, message }); return Failure("Message empty", "IM", new [] { avatar, message });
} }
bot.SendIM(avataruuid, message); master.botClient.SendIM(avataruuid, message);
return BasicReply("ok", "IM", new [] { avatar, message }); return BasicReply("ok", "IM", new [] { avatar, message });
} }

View file

@ -25,30 +25,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (target == "clear") return Failure("@todo dialog relay");
{
bot.SetRelayDialogsAvatar(UUID.Zero);
bot.SetRelayDialogsChannel(0);
bot.SetRelayDialogsHTTP("");
return BasicReply("cleared", "DialogRelay", new [] { target });
}
ProcessAvatar(target);
if (avataruuid != UUID.Zero)
{
bot.SetRelayDialogsAvatar(avataruuid);
return BasicReply("set/avatar [ok]", "DialogRelay", new [] { target });
}
if (target.StartsWith("http") == true)
{
bot.SetRelayDialogsHTTP(target);
return BasicReply("set/http [ok]", "DialogRelay", new [] { target });
}
if (int.TryParse(target, out int channel) == true)
{
bot.SetRelayDialogsChannel(channel);
return BasicReply("set/channel [ok]", "DialogRelay", new [] { target });
}
return Failure("Not a vaild option", "DialogRelay", new [] { target });
} }
@ -69,7 +46,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("bad dialog id", "DialogResponce", new [] { dialogid, buttontext }); return Failure("bad dialog id", "DialogResponce", new [] { dialogid, buttontext });
} }
return BasicReply(bot.DialogReply(dialogidnum, buttontext).ToString(), "DialogResponce", new [] { dialogid, buttontext }); return Failure("@todo dialog relay");
} }
[About("Should the bot track dialogs and send them to the relays setup?")] [About("Should the bot track dialogs and send them to the relays setup?")]

View file

@ -8,9 +8,9 @@ using System.Collections.Generic;
namespace SecondBotEvents.Commands namespace SecondBotEvents.Commands
{ {
public class Discord : CommandsAPI public class DiscordCommands : CommandsAPI
{ {
public Discord(EventsSecondBot setmaster) : base(setmaster) public DiscordCommands(EventsSecondBot setmaster) : base(setmaster)
{ {
} }
[About("Adds a discord server role to the selected member")] [About("Adds a discord server role to the selected member")]
@ -26,11 +26,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_AddRole", new [] { serverid, roleid, memberid }); return Failure("Discord client not ready");
} }
return BasicReply(addRoleToMember(serverid, roleid, memberid).Result.ToString(), "Discord_AddRole", new [] { serverid, roleid, memberid }); return BasicReply(master.DiscordService.addRoleToMember(serverid, roleid, memberid).Result.ToString(), "Discord_AddRole", new [] { serverid, roleid, memberid });
} }
[About("Adds the selected user to the ban list - Disallows rejoining untill they are removed from the list")] [About("Adds the selected user to the ban list - Disallows rejoining untill they are removed from the list")]
@ -47,15 +47,15 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_BanMember", new [] { serverid, memberid, why }); return Failure("Discord client not ready");
} }
if (SecondbotHelpers.notempty(why) == false) if (SecondbotHelpers.notempty(why) == false)
{ {
return Failure("Why empty", "Discord_BanMember", new [] { serverid, memberid, why }); return Failure("Why empty", "Discord_BanMember", new [] { serverid, memberid, why });
} }
return BasicReply(BanMember(serverid, memberid, why).Result.ToString(), "Discord_BanMember", new [] { serverid, memberid, why }); return BasicReply(master.DiscordService.BanMember(serverid, memberid, why).Result.ToString(), "Discord_BanMember", new [] { serverid, memberid, why });
} }
[About("Clears messages on the server sent by the member in the last 13 days, 22hours 59mins")] [About("Clears messages on the server sent by the member in the last 13 days, 22hours 59mins")]
@ -70,12 +70,12 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_BulkClear_Messages", new [] { serverid, memberid }); return Failure("Discord client not ready");
} }
SuccessNoReturn("Discord_BulkClear_Messages", new [] { serverid, memberid }); SuccessNoReturn("Discord_BulkClear_Messages", new [] { serverid, memberid });
return ClearMessages(serverid, memberid).Result; return master.DiscordService.ClearMessages(serverid, memberid).Result;
} }
[About("Sends a message directly to the user [They must be in the server]\n This command requires the SERVER MEMBERS INTENT found in discord app dev")] [About("Sends a message directly to the user [They must be in the server]\n This command requires the SERVER MEMBERS INTENT found in discord app dev")]
@ -91,11 +91,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_Dm_Member", new [] { serverid, memberid, message }); return Failure("Discord client not ready");
} }
return BasicReply(MessageMember(serverid, memberid, message).Result.ToString(), "Discord_Dm_Member", new [] { serverid, memberid, message }); return BasicReply(master.DiscordService.MessageMember(serverid, memberid, message).Result.ToString(), "Discord_Dm_Member", new [] { serverid, memberid, message });
} }
[About("Returns a list of members in a server \n collection is userid: username \n if the user has set a nickname: userid: nickname|username \n" + [About("Returns a list of members in a server \n collection is userid: username \n if the user has set a nickname: userid: nickname|username \n" +
@ -111,12 +111,12 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_MembersList", new [] { serverid }); return Failure("Discord client not ready");
} }
SuccessNoReturn("Discord_MembersList", new [] { serverid }); SuccessNoReturn("Discord_MembersList", new [] { serverid });
return ListMembers(serverid).Result; return master.DiscordService.ListMembers(serverid).Result;
} }
[About("Sends a message to the selected channel - Optional TTS usage")] [About("Sends a message to the selected channel - Optional TTS usage")]
@ -134,15 +134,15 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_MessageChannel", new [] { serverid, channelid, tts, message }); return Failure("Discord client not ready");
} }
if (SecondbotHelpers.notempty(message) == false) if (SecondbotHelpers.notempty(message) == false)
{ {
return Failure("message empty", "Discord_MessageChannel", new [] { serverid, channelid, tts, message }); return Failure("message empty", "Discord_MessageChannel", new [] { serverid, channelid, tts, message });
} }
return BasicReply(MessageChannel(serverid, channelid, message, tts).Result.ToString(), "Discord_MessageChannel", new [] { serverid, channelid, tts, message }); return BasicReply(master.DiscordService.MessageChannel(serverid, channelid, message, tts).Result.ToString(), "Discord_MessageChannel", new [] { serverid, channelid, tts, message });
} }
[About("Sends a message to the selected channel - Optional TTS usage")] [About("Sends a message to the selected channel - Optional TTS usage")]
@ -158,11 +158,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_MuteMember", new [] { serverid, memberid, mode }); return Failure("Discord client not ready");
} }
return BasicReply(MuteMember(serverid, memberid, mode).Result.ToString(), "Discord_MuteMember", new [] { serverid, memberid, mode }); return BasicReply(master.DiscordService.MuteMember(serverid, memberid, mode).Result.ToString(), "Discord_MuteMember", new [] { serverid, memberid, mode });
} }
[About("returns a collection of settings for the given role \n This command requires Discord full client mode enabled and connected")] [About("returns a collection of settings for the given role \n This command requires Discord full client mode enabled and connected")]
@ -178,12 +178,12 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_Role_GetSettings", new [] { serverid, roleid }); return Failure("Discord client not ready");
} }
SuccessNoReturn("Discord_Role_GetSettings", new [] { serverid, roleid }); SuccessNoReturn("Discord_Role_GetSettings", new [] { serverid, roleid });
return RoleSettings(serverid,roleid); return master.DiscordService.RoleSettings(serverid,roleid);
} }
[About("Updates perm flags for the selected role \n example CSV format: Speak=True,SendMessages=False \n for a full list of perms see output of Discord_Role_GetSettings \n" + [About("Updates perm flags for the selected role \n example CSV format: Speak=True,SendMessages=False \n for a full list of perms see output of Discord_Role_GetSettings \n" +
@ -200,11 +200,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_Role_UpdatePerms", new [] { serverid, roleid, flagscsv }); return Failure("Discord client not ready");
} }
return BasicReply(UpdateRolePerms(serverid, roleid, flagscsv).Result.ToString(), "Discord_Role_UpdatePerms", new [] { serverid, roleid, flagscsv }); return BasicReply(master.DiscordService.UpdateRolePerms(serverid, roleid, flagscsv).Result.ToString(), "Discord_Role_UpdatePerms", new [] { serverid, roleid, flagscsv });
} }
[About("Updates perm flags for the selected role \n example CSV format: Speak=True,SendMessages=False \n for a full list of perms see output of Discord_Role_GetSettings \n" + [About("Updates perm flags for the selected role \n example CSV format: Speak=True,SendMessages=False \n for a full list of perms see output of Discord_Role_GetSettings \n" +
@ -220,12 +220,12 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_RoleCreate", new [] { serverid, role }); return Failure("Discord client not ready");
} }
SuccessNoReturn("Discord_RoleCreate", new [] { serverid, role }); SuccessNoReturn("Discord_RoleCreate", new [] { serverid, role });
return CreateRole(serverid,role).Result; return master.DiscordService.CreateRole(serverid,role).Result;
} }
[About("Returns a list of roles and their ids in collection \n This command requires Discord full client mode enabled and connected")] [About("Returns a list of roles and their ids in collection \n This command requires Discord full client mode enabled and connected")]
@ -239,11 +239,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_RoleList", new [] { serverid }); return Failure("Discord client not ready");
} }
return BasicReply(ListRoles(serverid).ToString(), "Discord_RoleList", new [] { serverid }); return BasicReply(master.DiscordService.ListRoles(serverid).ToString(), "Discord_RoleList", new [] { serverid });
} }
[About("Remove a role from a server \n This command requires Discord full client mode enabled and connected")] [About("Remove a role from a server \n This command requires Discord full client mode enabled and connected")]
@ -257,11 +257,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_RoleRemove", new [] { serverid, roleid }); return Failure("Discord client not ready");
} }
return BasicReply(RemoveRole(serverid, roleid).Result.ToString(), "Discord_RoleRemove", new [] { serverid, roleid }); return BasicReply(master.DiscordService.RemoveRole(serverid, roleid).Result.ToString(), "Discord_RoleRemove", new [] { serverid, roleid });
} }
[About("Returns a list of text channels in a server")] [About("Returns a list of text channels in a server")]
@ -275,436 +275,11 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.discordReady() == false) if (master.DiscordService.DiscordReady() == false)
{ {
return Failure("Discord client not ready", "Discord_TextChannels_List", new [] { serverid }); return Failure("Discord client not ready");
} }
return ListTextChannels(serverid); return master.DiscordService.ListTextChannels(serverid);
} }
#region discord helper functions
protected KeyValuePair<bool, Dictionary<string, string>> ListTextChannels(string givenserverid)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == true)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = Discord.GetGuild(serverid);
Dictionary<string, string> textChannels = new Dictionary<string, string>();
foreach (SocketTextChannel channel in server.TextChannels)
{
textChannels.Add(channel.Id.ToString(), channel.Name);
}
return new KeyValuePair<bool, Dictionary<string, string>>(true, textChannels);
}
protected async Task<bool> RemoveRole(string givenserverid, string givenroleid)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenroleid, out ulong roleid) == false)
{
return false;
}
SocketGuild server = Discord.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
await role.DeleteAsync().ConfigureAwait(true);
return true;
}
protected KeyValuePair<bool, Dictionary<string, string>> ListRoles(string givenserverid)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = Discord.GetGuild(serverid);
Dictionary<string, string> rolesList = new Dictionary<string, string>();
foreach (SocketRole role in server.Roles)
{
rolesList.Add(role.Id.ToString(), role.Name);
}
return new KeyValuePair<bool, Dictionary<string, string>>(true, rolesList);
}
protected async Task<KeyValuePair<string, ulong>> CreateRole(string givenserverid, string givenrole)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == true)
{
return new KeyValuePair<string, ulong>("Unable to process server id", 0);
}
SocketGuild server = Discord.GetGuild(serverid);
RestRole role = await server.CreateRoleAsync(givenrole, new GuildPermissions(), Color.DarkRed, false, null).ConfigureAwait(true);
return new KeyValuePair<string, ulong>("ok", role.Id);
}
protected async Task<bool> UpdateRolePerms(string givenserverid,string givenroleid,string givenflagscsv)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenroleid, out ulong roleid) == true)
{
return false;
}
SocketGuild server = Discord.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
string[] bits = givenflagscsv.Split(',');
GuildPermissions NewPerms = role.Permissions;
foreach (String bit in bits)
{
string[] subbits = bit.Split('=');
if (subbits.Length == 2)
{
if (bool.TryParse(subbits[1], out bool flagValue) == true)
{
NewPerms = UpdateFlag(NewPerms, subbits[0], flagValue);
}
}
}
await role.ModifyAsync(Rp =>
{
Rp.Permissions = NewPerms;
}).ConfigureAwait(true);
return true;
}
protected GuildPermissions UpdateFlag(GuildPermissions NewPerms, string flag, bool flagValue)
{
switch (flag)
{
case "CreateInstantInvite":
NewPerms = NewPerms.Modify(flagValue);
break;
case "KickMembers":
NewPerms = NewPerms.Modify(null, flagValue);
break;
case "BanMembers":
NewPerms = NewPerms.Modify(null, null, flagValue);
break;
case "Administrator":
NewPerms = NewPerms.Modify(null, null, null, flagValue);
break;
case "ManageChannels":
NewPerms = NewPerms.Modify(null, null, null, null, flagValue);
break;
case "ManageGuild":
NewPerms = NewPerms.Modify(null, null, null, null, null, flagValue);
break;
case "AddReactions":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, flagValue);
break;
case "ViewAuditLog":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, flagValue);
break;
case "SendTTSMessages":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "AttachFiles":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ReadMessageHistory":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "MentionEveryone":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "UseExternalEmojis":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "Connect":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "Speak":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "MuteMembers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "UseVAD":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "MoveMembers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "EmbedLinks":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "PrioritySpeaker":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "Stream":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ChangeNickname":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageNicknames":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageRoles":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "DeafenMembers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageMessages":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ViewChannel":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, flagValue);
break;
case "SendMessages":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageWebhooks":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageEmojis":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
default:
break;
}
return NewPerms;
}
protected KeyValuePair<bool, Dictionary<string, string>> RoleSettings(string givenserverid, string givenroleid)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
if (ulong.TryParse(givenroleid, out ulong roleid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = Discord.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
Dictionary<string, string> settings = new Dictionary<string, string>
{
{ "Config|Color", String.Join(",", new byte[3] { role.Color.R, role.Color.G, role.Color.B }) },
{ "Config|Name", role.Name },
{ "Info|AssignedCount", role.Members.ToString() },
{ "Perm|AttachFiles", role.Permissions.AttachFiles.ToString() },
{ "Perm|ReadMessageHistory", role.Permissions.ReadMessageHistory.ToString() },
{ "Perm|MentionEveryone", role.Permissions.MentionEveryone.ToString() },
{ "Perm|UseExternalEmojis", role.Permissions.UseExternalEmojis.ToString() },
{ "Perm|Connect", role.Permissions.Connect.ToString() },
{ "Perm|Speak", role.Permissions.Speak.ToString() },
{ "Perm|MuteMembers", role.Permissions.MuteMembers.ToString() },
{ "Perm|UseVAD", role.Permissions.UseVAD.ToString() },
{ "Perm|MoveMembers", role.Permissions.MoveMembers.ToString() },
{ "Perm|EmbedLinks", role.Permissions.EmbedLinks.ToString() },
{ "Perm|PrioritySpeaker", role.Permissions.PrioritySpeaker.ToString() },
{ "Perm|Stream", role.Permissions.Stream.ToString() },
{ "Perm|ChangeNickname", role.Permissions.ChangeNickname.ToString() },
{ "Perm|ManageNicknames", role.Permissions.ManageNicknames.ToString() },
{ "Perm|ManageRoles", role.Permissions.ManageRoles.ToString() },
{ "Perm|DeafenMembers", role.Permissions.DeafenMembers.ToString() },
{ "Perm|ManageMessages", role.Permissions.ManageMessages.ToString() },
{ "Perm|ViewChannel", role.Permissions.ViewChannel.ToString() },
{ "Perm|SendMessages", role.Permissions.SendMessages.ToString() },
{ "Perm|CreateInstantInvite", role.Permissions.CreateInstantInvite.ToString() },
{ "Perm|BanMembers", role.Permissions.BanMembers.ToString() },
{ "Perm|SendTTSMessages", role.Permissions.SendTTSMessages.ToString() },
{ "Perm|Administrator", role.Permissions.Administrator.ToString() },
{ "Perm|ManageChannels", role.Permissions.ManageChannels.ToString() },
{ "Perm|KickMembers", role.Permissions.KickMembers.ToString() },
{ "Perm|AddReactions", role.Permissions.AddReactions.ToString() },
{ "Perm|ViewAuditLog", role.Permissions.ViewAuditLog.ToString() },
{ "Perm|ManageWebhooks", role.Permissions.ManageWebhooks.ToString() },
{ "Perm|ManageGuild", role.Permissions.ManageGuild.ToString() },
{ "Perm|ManageEmojis", role.Permissions.ManageEmojis.ToString() }
};
return new KeyValuePair<bool, Dictionary<string, string>>(true, settings);
}
protected async Task<bool> MuteMember(string givenserverid, string givenmemberid, string mode)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == true)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == true)
{
return false;
}
SocketGuild server = Discord.GetGuild(serverid);
SocketGuildUser user = server.GetUser(memberid);
bool.TryParse(mode, out bool status);
await user.ModifyAsync(pr =>
{
pr.Mute = status;
});
return true;
}
protected async Task<bool> MessageChannel(string givenserverid, string givenchannelid, string givenmessage, string tts)
{
if (bool.TryParse(tts, out bool useTTS) == false)
{
return false;
}
return await bot.SendMessageToDiscord(givenserverid, givenchannelid, givenmessage, useTTS);
}
protected async Task<KeyValuePair<bool, Dictionary<string, string>>> ListMembers(string givenserverid)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = Discord.GetGuild(serverid);
IEnumerable<IGuildUser> users = await server.GetUsersAsync().FlattenAsync().ConfigureAwait(true);
Dictionary<string, string> membersList = new Dictionary<string, string>();
foreach (IGuildUser user in users)
{
if (user.Nickname != null)
{
membersList.Add(user.Id.ToString(), user.Nickname + "|" + user.Username);
}
else
{
membersList.Add(user.Id.ToString(), user.Username);
}
}
return new KeyValuePair<bool, Dictionary<string, string>>(true, membersList);
}
protected async Task<bool> MessageMember(string givenserverid, string givenmemberid, string givenmessage)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return false;
}
try
{
SocketGuild server = Discord.GetGuild(serverid);
IEnumerable<IGuildUser> users = await server.GetUsersAsync().FlattenAsync().ConfigureAwait(true);
bool sent = false;
foreach (IGuildUser user in users)
{
if (user.Id == memberid)
{
sent = true;
await user.SendMessageAsync(givenmessage);
break;
}
}
return sent;
}
catch
{
return false;
}
}
protected async Task<KeyValuePair<bool, int>> ClearMessages(string givenserverid, string givenmemberid)
{
int messagesDeleted = 0;
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, int>(false, 0);
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return new KeyValuePair<bool, int>(false, 0);
}
SocketGuild server = Discord.GetGuild(serverid);
SocketGuildUser user = server.GetUser(memberid);
TimeSpan somedays = new TimeSpan(13, 22, 59, 59, 0);
DateTimeOffset justundertwosweeks = DateTimeOffset.Now;
justundertwosweeks = justundertwosweeks.Subtract(somedays);
long unixtimelimit = justundertwosweeks.ToUnixTimeSeconds();
foreach (SocketTextChannel channel in server.TextChannels)
{
List<IMessage> DeleteMessages = new List<IMessage>();
IEnumerable<IMessage> messages = await channel.GetMessagesAsync(100).FlattenAsync();
foreach (IMessage message in messages)
{
if (message.CreatedAt.ToUnixTimeSeconds() > unixtimelimit)
{
if (message.Author.Id == user.Id)
{
DeleteMessages.Add(message);
}
}
}
if (DeleteMessages.Count > 0)
{
messagesDeleted += DeleteMessages.Count;
await channel.DeleteMessagesAsync(DeleteMessages);
}
}
return new KeyValuePair<bool, int>(true, messagesDeleted);
}
protected async Task<bool> BanMember(string givenserverid, string givenmemberid, string why)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return false;
}
SocketGuild server = Discord.GetGuild(serverid);
SocketGuildUser user = server.GetUser(memberid);
await user.BanAsync(7, why);
return true;
}
protected async Task<bool> addRoleToMember(string givenserverid, string givenroleid, string givenmemberid)
{
DiscordSocketClient Discord = bot.getDiscordClient();
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenroleid, out ulong roleid) == false)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return false;
}
SocketGuild server = Discord.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
SocketGuildUser user = server.GetUser(memberid);
await user.AddRoleAsync(role); // ? Irole seems to accept SocketRole ?
return true;
}
#endregion
} }
} }

View file

@ -1,9 +1,10 @@
using EmbedIO; using EmbedIO;
using EmbedIO.Routing; using EmbedIO.Routing;
using EmbedIO.WebApi; using EmbedIO.WebApi;
using Newtonsoft.Json;
using OpenMetaverse; using OpenMetaverse;
using SecondBotEvents.Services; using SecondBotEvents.Services;
using System.Collections.Generic;
namespace SecondBotEvents.Commands namespace SecondBotEvents.Commands
{ {
@ -138,7 +139,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
return BasicReply(JsonConvert.SerializeObject(bot._lastUpdatedSimBlacklist), "banlist"); return Failure("@todo estate blacklist");
} }
[About("Attempts to add/remove the avatar to/from the Estate banlist")] [About("Attempts to add/remove the avatar to/from the Estate banlist")]

View file

@ -21,7 +21,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.GetAllowFunds == false) if (master.CommandsService.myConfig.GetAllowFundsCommands() == false)
{ {
return Failure("Funds commands are disabled", "Balance"); return Failure("Funds commands are disabled", "Balance");
} }
@ -44,7 +44,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.GetAllowFunds == false) if (master.CommandsService.myConfig.GetAllowFundsCommands() == false)
{ {
return Failure("Transfer funds to avatars disabled", "PayAvatar", new [] { avatar, amount }); return Failure("Transfer funds to avatars disabled", "PayAvatar", new [] { avatar, amount });
} }
@ -82,7 +82,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
if (bot.GetAllowFunds == false) if (master.CommandsService.myConfig.GetAllowFundsCommands() == false)
{ {
return Failure("Funds commands are disabled", "PayObject", new [] { objectuuid, primname, amount }); return Failure("Funds commands are disabled", "PayObject", new [] { objectuuid, primname, amount });
} }

View file

@ -632,6 +632,9 @@ namespace SecondBotEvents.Commands
protected KeyValuePair<bool, string> waitForReady(bool avatar, bool group, UUID groupuuid) protected KeyValuePair<bool, string> waitForReady(bool avatar, bool group, UUID groupuuid)
{ {
/* this function is poorly named
* and is ugly :(
*/
bool exit = false; bool exit = false;
int sleeps = 0; int sleeps = 0;
string failed_on = ""; string failed_on = "";
@ -650,7 +653,8 @@ namespace SecondBotEvents.Commands
} }
if ((group == true) && (allok == true)) if ((group == true) && (allok == true))
{ {
allok = !bot.NeedReloadGroupData(groupuuid); // @todo wait for group loading
allok = true;
failed_on = "Updating"; failed_on = "Updating";
} }
if (allok == false) if (allok == false)

View file

@ -86,7 +86,7 @@ namespace SecondBotEvents.Commands
{ {
return Failure("Token not accepted"); return Failure("Token not accepted");
} }
return BasicReply(bot.MyVersion, "Version"); return BasicReply(master.GetVersion(), "Version");
} }

View file

@ -1,11 +1,14 @@
using EmbedIO; using BetterSecondBot.Static;
using EmbedIO;
using EmbedIO.Routing; using EmbedIO.Routing;
using EmbedIO.WebApi; using EmbedIO.WebApi;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Assets; using OpenMetaverse.Assets;
using SecondBotEvents.Services; using SecondBotEvents.Services;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
namespace SecondBotEvents.Commands namespace SecondBotEvents.Commands
{ {
@ -271,6 +274,7 @@ namespace SecondBotEvents.Commands
[ReturnHints("ok")] [ReturnHints("ok")]
[ReturnHints("Unable to find notecard folder")] [ReturnHints("Unable to find notecard folder")]
[Route(HttpVerbs.Get, "/InventoryPurgeNotecards/{token}")] [Route(HttpVerbs.Get, "/InventoryPurgeNotecards/{token}")]
[Obsolete]
public object InventoryPurgeNotecards(string token) public object InventoryPurgeNotecards(string token)
{ {
if (AllowToken(token) == false) if (AllowToken(token) == false)

View file

@ -1,9 +1,13 @@
using EmbedIO; using EmbedIO;
using EmbedIO.Routing; using EmbedIO.Routing;
using EmbedIO.WebApi; using EmbedIO.WebApi;
using Newtonsoft.Json;
using OpenMetaverse; using OpenMetaverse;
using SecondBotEvents.Services; using SecondBotEvents.Services;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using static OpenMetaverse.ParcelManager;
namespace SecondBotEvents.Commands namespace SecondBotEvents.Commands
{ {
@ -607,7 +611,8 @@ namespace SecondBotEvents.Commands
} }
foreach (ParcelAccessEntry e in targetparcel.AccessBlackList) foreach (ParcelAccessEntry e in targetparcel.AccessBlackList)
{ {
string name = bot.FindAvatarKey2Name(e.AgentID); // @todo add avatar key to name
string name = "?";
reply.entrys.Add(e.AgentID, name); reply.entrys.Add(e.AgentID, name);
} }
reply.reportedEntrys = targetparcel.AccessBlackList.Count; reply.reportedEntrys = targetparcel.AccessBlackList.Count;

View file

@ -260,10 +260,10 @@ namespace SecondBotEvents.Commands
} }
if (stateflag == true) if (stateflag == true)
{ {
bot.Add_action_from(flag, avataruuid, bot.FindAvatarKey2Name(avataruuid), stickyflag); // @todo add stick/normal accept dynamic actions
return BasicReply("Added perm: " + flag + " Sticky: " + stickyflag.ToString(), "SetPermFlag", new [] { avatar, flag, state, sticky }); return BasicReply("Added perm: " + flag + " Sticky: " + stickyflag.ToString(), "SetPermFlag", new [] { avatar, flag, state, sticky });
} }
bot.Remove_action_from(flag, avataruuid, stickyflag); // @todo remove stick/normal accept dynamic actions
return BasicReply("Removed perm: " + flag + " Sticky: " + stickyflag.ToString(), "SetPermFlag", new [] { avatar, flag, state, sticky }); return BasicReply("Removed perm: " + flag + " Sticky: " + stickyflag.ToString(), "SetPermFlag", new [] { avatar, flag, state, sticky });
} }

View file

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SecondBotEvents.Config
{
public class DiscordConfig : Config
{
public DiscordConfig(bool fromENV, string fromFolder = "") : base(fromENV, fromFolder) { }
protected override void MakeSettings()
{
filename = "discord";
settings.Add("enabled");
settings.Add("serverID");
settings.Add("clientToken");
settings.Add("allowDiscordCommands");
}
public bool GetEnabled()
{
return ReadSettingAsBool("enabled");
}
public ulong GetServerID()
{
return ReadSettingAsUlong("serverID", 0);
}
public string GetClientToken()
{
return ReadSettingAsString("clientToken","tokenPlzKThanks");
}
public bool GetAllowDiscordCommands()
{
return ReadSettingAsBool("allowDiscordCommands");
}
}
}

View file

@ -129,5 +129,15 @@ namespace SecondBotEvents.Config
} }
return value; return value;
} }
protected ulong ReadSettingAsUlong(string key, ulong defaultValue = 0)
{
bool result = ulong.TryParse(ReadSettingAsString(key, defaultValue.ToString()), out ulong value);
if (result == false)
{
return defaultValue;
}
return value;
}
} }
} }

View file

@ -25,6 +25,7 @@ namespace SecondBotEvents
public HttpService HttpService = null; public HttpService HttpService = null;
public CommandsService CommandsService = null; public CommandsService CommandsService = null;
public BotClientService botClient = null; public BotClientService botClient = null;
public DiscordService DiscordService = null;
private EventHandler<BotClientNotice> botclient_eventNotices; private EventHandler<BotClientNotice> botclient_eventNotices;
@ -74,6 +75,7 @@ namespace SecondBotEvents
botClient = new BotClientService(this); botClient = new BotClientService(this);
HttpService = new HttpService(this); HttpService = new HttpService(this);
CommandsService = new CommandsService(this); CommandsService = new CommandsService(this);
DiscordService = new DiscordService(this);
if (botClient.IsLoaded() == false) if (botClient.IsLoaded() == false)
{ {
Console.WriteLine("Config is not loaded :("); Console.WriteLine("Config is not loaded :(");
@ -82,8 +84,8 @@ namespace SecondBotEvents
} }
HttpService.Start(); HttpService.Start();
CommandsService.Start(); CommandsService.Start();
DiscordService.Start();
botClient.Start(); botClient.Start();
} }
protected void StopServices() protected void StopServices()
{ {
@ -91,12 +93,21 @@ namespace SecondBotEvents
{ {
HttpService.Stop(); HttpService.Stop();
} }
if (CommandsService != null)
{
CommandsService.Stop();
}
if (DiscordService != null)
{
DiscordService.Stop();
}
if(botClient != null) if(botClient != null)
{ {
botClient.Stop(); botClient.Stop();
} }
HttpService = null; HttpService = null;
botClient = null; botClient = null;
} }
public bool Exit() public bool Exit()

View file

@ -10,6 +10,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.4.1" />
<PackageReference Include="Discord.Net.WebSocket" Version="3.4.1" />
<PackageReference Include="EmbedIO" Version="3.4.3" /> <PackageReference Include="EmbedIO" Version="3.4.3" />
<PackageReference Include="MSBuildGitHash" Version="2.0.2"> <PackageReference Include="MSBuildGitHash" Version="2.0.2">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View file

@ -55,6 +55,11 @@ namespace SecondBotEvents.Services
{ {
Console.WriteLine("Client service ~ Login status: " + e.Status.ToString()); Console.WriteLine("Client service ~ Login status: " + e.Status.ToString());
} }
public void SendIM(UUID avatar, string message)
{
// @todo add message to IM chat
client.Self.InstantMessage(avatar, message);
}
protected void ResetClient() protected void ResetClient()
{ {

View file

@ -108,7 +108,12 @@ namespace SecondBotEvents.Services
case InstantMessageDialog.MessageFromAgent: // shared with SessionSend case InstantMessageDialog.MessageFromAgent: // shared with SessionSend
case InstantMessageDialog.SessionSend: case InstantMessageDialog.SessionSend:
{ {
if(myConfig.GetOnlySelectedAvs() == false) if(e.IM.GroupIM == false)
{
acceptMessage = false;
break;
}
if (myConfig.GetOnlySelectedAvs() == false)
{ {
acceptMessage = false; acceptMessage = false;
break; break;

View file

@ -0,0 +1,609 @@
using Discord;
using Discord.Rest;
using Discord.WebSocket;
using OpenMetaverse;
using SecondBotEvents.Config;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SecondBotEvents.Services
{
public class DiscordService : Services
{
public Config.DiscordConfig myConfig = null;
public bool acceptChat = false;
public bool discordIsReady = false;
protected DiscordSocketClient DiscordClient;
public DiscordService(EventsSecondBot setMaster) : base(setMaster)
{
myConfig = new Config.DiscordConfig(master.fromEnv, master.fromFolder);
}
public bool DiscordReady()
{
if(acceptChat == false)
{
return false;
}
return discordIsReady;
}
protected Task DiscordClientLoggedOut()
{
Console.WriteLine("Discord service [Logged out]");
discordIsReady = false;
return Task.CompletedTask;
}
protected Task DicordClientLoggedin()
{
Console.WriteLine("Discord service [Logged in]");
return Task.CompletedTask;
}
protected Task DiscordClientReady()
{
Console.WriteLine("Discord service [Ready]");
discordIsReady = true;
return Task.CompletedTask;
}
protected Task DiscordClientMessageReceived(SocketMessage message)
{
return Task.CompletedTask;
}
protected Task DiscordDisconnected(Exception e)
{
Console.WriteLine("Discord service [Disconnected: "+e.Message+"]");
discordIsReady = false;
return Task.CompletedTask;
}
public async override void Start()
{
if(myConfig.GetEnabled() == false)
{
Console.WriteLine("Discord service [Disabled]");
return;
}
Console.WriteLine("Discord service [Starting]");
master.BotClientNoticeEvent += BotClientRestart;
DiscordClient = new DiscordSocketClient();
DiscordClient.Ready += DiscordClientReady;
DiscordClient.LoggedOut += DiscordClientLoggedOut;
DiscordClient.LoggedIn += DicordClientLoggedin;
DiscordClient.MessageReceived += DiscordClientMessageReceived;
DiscordClient.Disconnected += DiscordDisconnected;
await DiscordClient.LoginAsync(TokenType.Bot, myConfig.GetClientToken());
}
public override void Stop()
{
Console.WriteLine("Discord service [Stopping]");
master.BotClientNoticeEvent -= BotClientRestart;
acceptChat = false;
if(DiscordClient != null)
{
DiscordClient.Dispose();
DiscordClient = null;
}
}
protected void BotClientRestart(object o, BotClientNotice e)
{
Console.WriteLine("Discord service [Avi link - Restarting]");
acceptChat = false;
master.botClient.client.Network.LoggedOut += BotLoggedOut;
master.botClient.client.Network.SimConnected += BotLoggedIn;
}
protected void BotLoggedOut(object o, LoggedOutEventArgs e)
{
master.botClient.client.Network.SimConnected += BotLoggedIn;
acceptChat = false;
Console.WriteLine("Discord service [Avi link - standby]");
}
protected void BotImMessage(object o, InstantMessageEventArgs e)
{
if(acceptChat == false)
{
return;
}
switch (e.IM.Dialog)
{
case InstantMessageDialog.MessageFromObject:
{
objectChat(e.IM.FromAgentName, e.IM.Message);
break;
}
case InstantMessageDialog.MessageFromAgent: // shared with SessionSend
case InstantMessageDialog.SessionSend:
{
if(e.IM.GroupIM == false)
{
avatarChat(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.Message);
break;
}
groupChat(e.IM.IMSessionID, e.IM.FromAgentName, e.IM.Message);
break;
}
default:
{
break;
}
}
}
protected void groupChat(UUID group, string name, string message)
{
}
protected void avatarChat(UUID avatar, string name, string message)
{
}
protected void objectChat(string objectName, string message)
{
}
protected void statusChat(string message)
{
}
protected bool acceptMessageSigned(string message)
{
return false;
}
protected void BotLoggedIn(object o, SimConnectedEventArgs e)
{
acceptChat = true;
master.botClient.client.Network.SimConnected -= BotLoggedIn;
master.botClient.client.Self.IM += BotImMessage;
Console.WriteLine("Discord service [Avi link - Active]");
}
#region discord helper functions
public KeyValuePair<bool, Dictionary<string, string>> ListTextChannels(string givenserverid)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == true)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = DiscordClient.GetGuild(serverid);
Dictionary<string, string> textChannels = new Dictionary<string, string>();
foreach (SocketTextChannel channel in server.TextChannels)
{
textChannels.Add(channel.Id.ToString(), channel.Name);
}
return new KeyValuePair<bool, Dictionary<string, string>>(true, textChannels);
}
public async Task<bool> RemoveRole(string givenserverid, string givenroleid)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenroleid, out ulong roleid) == false)
{
return false;
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
await role.DeleteAsync().ConfigureAwait(true);
return true;
}
public KeyValuePair<bool, Dictionary<string, string>> ListRoles(string givenserverid)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = DiscordClient.GetGuild(serverid);
Dictionary<string, string> rolesList = new Dictionary<string, string>();
foreach (SocketRole role in server.Roles)
{
rolesList.Add(role.Id.ToString(), role.Name);
}
return new KeyValuePair<bool, Dictionary<string, string>>(true, rolesList);
}
public async Task<KeyValuePair<string, ulong>> CreateRole(string givenserverid, string givenrole)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == true)
{
return new KeyValuePair<string, ulong>("Unable to process server id", 0);
}
SocketGuild server = DiscordClient.GetGuild(serverid);
RestRole role = await server.CreateRoleAsync(givenrole, new GuildPermissions(), Color.DarkRed, false, false).ConfigureAwait(true);
return new KeyValuePair<string, ulong>("ok", role.Id);
}
public async Task<bool> UpdateRolePerms(string givenserverid, string givenroleid, string givenflagscsv)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenroleid, out ulong roleid) == true)
{
return false;
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
string[] bits = givenflagscsv.Split(',');
GuildPermissions NewPerms = role.Permissions;
foreach (String bit in bits)
{
string[] subbits = bit.Split('=');
if (subbits.Length == 2)
{
if (bool.TryParse(subbits[1], out bool flagValue) == true)
{
NewPerms = UpdateFlag(NewPerms, subbits[0], flagValue);
}
}
}
await role.ModifyAsync(Rp =>
{
Rp.Permissions = NewPerms;
}).ConfigureAwait(true);
return true;
}
protected GuildPermissions UpdateFlag(GuildPermissions NewPerms, string flag, bool flagValue)
{
switch (flag)
{
case "CreateInstantInvite":
NewPerms = NewPerms.Modify(flagValue);
break;
case "KickMembers":
NewPerms = NewPerms.Modify(null, flagValue);
break;
case "BanMembers":
NewPerms = NewPerms.Modify(null, null, flagValue);
break;
case "Administrator":
NewPerms = NewPerms.Modify(null, null, null, flagValue);
break;
case "ManageChannels":
NewPerms = NewPerms.Modify(null, null, null, null, flagValue);
break;
case "ManageGuild":
NewPerms = NewPerms.Modify(null, null, null, null, null, flagValue);
break;
case "AddReactions":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, flagValue);
break;
case "ViewAuditLog":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, flagValue);
break;
case "SendTTSMessages":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "AttachFiles":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ReadMessageHistory":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "MentionEveryone":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "UseExternalEmojis":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "Connect":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "Speak":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "MuteMembers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "UseVAD":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "MoveMembers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "EmbedLinks":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "PrioritySpeaker":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "Stream":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ChangeNickname":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageNicknames":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageRoles":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "DeafenMembers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageMessages":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ViewChannel":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, flagValue);
break;
case "SendMessages":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageWebhooks":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
case "ManageEmojisAndStickers":
NewPerms = NewPerms.Modify(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, flagValue);
break;
default:
break;
}
return NewPerms;
}
public KeyValuePair<bool, Dictionary<string, string>> RoleSettings(string givenserverid, string givenroleid)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
if (ulong.TryParse(givenroleid, out ulong roleid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
Dictionary<string, string> settings = new Dictionary<string, string>
{
{ "Config|Color", String.Join(",", new byte[3] { role.Color.R, role.Color.G, role.Color.B }) },
{ "Config|Name", role.Name },
{ "Info|AssignedCount", role.Members.ToString() },
{ "Perm|AttachFiles", role.Permissions.AttachFiles.ToString() },
{ "Perm|ReadMessageHistory", role.Permissions.ReadMessageHistory.ToString() },
{ "Perm|MentionEveryone", role.Permissions.MentionEveryone.ToString() },
{ "Perm|UseExternalEmojis", role.Permissions.UseExternalEmojis.ToString() },
{ "Perm|Connect", role.Permissions.Connect.ToString() },
{ "Perm|Speak", role.Permissions.Speak.ToString() },
{ "Perm|MuteMembers", role.Permissions.MuteMembers.ToString() },
{ "Perm|UseVAD", role.Permissions.UseVAD.ToString() },
{ "Perm|MoveMembers", role.Permissions.MoveMembers.ToString() },
{ "Perm|EmbedLinks", role.Permissions.EmbedLinks.ToString() },
{ "Perm|PrioritySpeaker", role.Permissions.PrioritySpeaker.ToString() },
{ "Perm|Stream", role.Permissions.Stream.ToString() },
{ "Perm|ChangeNickname", role.Permissions.ChangeNickname.ToString() },
{ "Perm|ManageNicknames", role.Permissions.ManageNicknames.ToString() },
{ "Perm|ManageRoles", role.Permissions.ManageRoles.ToString() },
{ "Perm|DeafenMembers", role.Permissions.DeafenMembers.ToString() },
{ "Perm|ManageMessages", role.Permissions.ManageMessages.ToString() },
{ "Perm|ViewChannel", role.Permissions.ViewChannel.ToString() },
{ "Perm|SendMessages", role.Permissions.SendMessages.ToString() },
{ "Perm|CreateInstantInvite", role.Permissions.CreateInstantInvite.ToString() },
{ "Perm|BanMembers", role.Permissions.BanMembers.ToString() },
{ "Perm|SendTTSMessages", role.Permissions.SendTTSMessages.ToString() },
{ "Perm|Administrator", role.Permissions.Administrator.ToString() },
{ "Perm|ManageChannels", role.Permissions.ManageChannels.ToString() },
{ "Perm|KickMembers", role.Permissions.KickMembers.ToString() },
{ "Perm|AddReactions", role.Permissions.AddReactions.ToString() },
{ "Perm|ViewAuditLog", role.Permissions.ViewAuditLog.ToString() },
{ "Perm|ManageWebhooks", role.Permissions.ManageWebhooks.ToString() },
{ "Perm|ManageGuild", role.Permissions.ManageGuild.ToString() },
{ "Perm|ManageEmojisAndStickers", role.Permissions.ManageEmojisAndStickers.ToString() }
};
return new KeyValuePair<bool, Dictionary<string, string>>(true, settings);
}
public async Task<bool> MuteMember(string givenserverid, string givenmemberid, string mode)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == true)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == true)
{
return false;
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketGuildUser user = server.GetUser(memberid);
bool.TryParse(mode, out bool status);
await user.ModifyAsync(pr =>
{
pr.Mute = status;
});
return true;
}
public async Task<bool> MessageChannel(string givenserverid, string givenchannelid, string givenmessage, string tts)
{
if (bool.TryParse(tts, out bool useTTS) == false)
{
return false;
}
return await SendMessageToDiscord(givenserverid, givenchannelid, givenmessage, useTTS);
}
protected async Task<bool> SendMessageToDiscord(string target_serverid, string target_channelid, string message, bool useTTS)
{
if (ulong.TryParse(target_serverid, out ulong serverid) == true)
{
if (ulong.TryParse(target_channelid, out ulong channelid) == true)
{
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketTextChannel Channel = null;
foreach (SocketTextChannel channel in server.TextChannels)
{
if (channel.Id == channelid)
{
Channel = channel;
break;
}
}
if (Channel != null)
{
await Channel.SendMessageAsync(message, useTTS);
return true;
}
}
}
return false;
}
public async Task<KeyValuePair<bool, Dictionary<string, string>>> ListMembers(string givenserverid)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, Dictionary<string, string>>(false, new Dictionary<string, string>());
}
SocketGuild server = DiscordClient.GetGuild(serverid);
IEnumerable<IGuildUser> users = await server.GetUsersAsync().FlattenAsync().ConfigureAwait(true);
Dictionary<string, string> membersList = new Dictionary<string, string>();
foreach (IGuildUser user in users)
{
if (user.Nickname != null)
{
membersList.Add(user.Id.ToString(), user.Nickname + "|" + user.Username);
}
else
{
membersList.Add(user.Id.ToString(), user.Username);
}
}
return new KeyValuePair<bool, Dictionary<string, string>>(true, membersList);
}
public async Task<bool> MessageMember(string givenserverid, string givenmemberid, string givenmessage)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return false;
}
try
{
SocketGuild server = DiscordClient.GetGuild(serverid);
IEnumerable<IGuildUser> users = await server.GetUsersAsync().FlattenAsync().ConfigureAwait(true);
bool sent = false;
foreach (IGuildUser user in users)
{
if (user.Id == memberid)
{
sent = true;
await user.SendMessageAsync(givenmessage);
break;
}
}
return sent;
}
catch
{
return false;
}
}
public async Task<KeyValuePair<bool, int>> ClearMessages(string givenserverid, string givenmemberid)
{
int messagesDeleted = 0;
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return new KeyValuePair<bool, int>(false, 0);
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return new KeyValuePair<bool, int>(false, 0);
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketGuildUser user = server.GetUser(memberid);
TimeSpan somedays = new TimeSpan(13, 22, 59, 59, 0);
DateTimeOffset justundertwosweeks = DateTimeOffset.Now;
justundertwosweeks = justundertwosweeks.Subtract(somedays);
long unixtimelimit = justundertwosweeks.ToUnixTimeSeconds();
foreach (SocketTextChannel channel in server.TextChannels)
{
List<IMessage> DeleteMessages = new List<IMessage>();
IEnumerable<IMessage> messages = await channel.GetMessagesAsync(100).FlattenAsync();
foreach (IMessage message in messages)
{
if (message.CreatedAt.ToUnixTimeSeconds() > unixtimelimit)
{
if (message.Author.Id == user.Id)
{
DeleteMessages.Add(message);
}
}
}
if (DeleteMessages.Count > 0)
{
messagesDeleted += DeleteMessages.Count;
await channel.DeleteMessagesAsync(DeleteMessages);
}
}
return new KeyValuePair<bool, int>(true, messagesDeleted);
}
public async Task<bool> BanMember(string givenserverid, string givenmemberid, string why)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return false;
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketGuildUser user = server.GetUser(memberid);
await user.BanAsync(7, why);
return true;
}
public async Task<bool> addRoleToMember(string givenserverid, string givenroleid, string givenmemberid)
{
if (ulong.TryParse(givenserverid, out ulong serverid) == false)
{
return false;
}
if (ulong.TryParse(givenroleid, out ulong roleid) == false)
{
return false;
}
if (ulong.TryParse(givenmemberid, out ulong memberid) == false)
{
return false;
}
SocketGuild server = DiscordClient.GetGuild(serverid);
SocketRole role = server.GetRole(roleid);
SocketGuildUser user = server.GetUser(memberid);
await user.AddRoleAsync(role); // ? Irole seems to accept SocketRole ?
return true;
}
#endregion
}
}

View file

@ -0,0 +1,193 @@
using Newtonsoft.Json;
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterSecondBot.Static
{
public class InventoryMapFolder
{
public string id;
public string name;
public List<InventoryMapFolder> subfolders = new List<InventoryMapFolder>();
}
public class InventoryMapItem
{
public string id;
public string name;
public string typename;
}
public static class HelperInventory
{
public static InventoryItem getItemByInventoryUUID(GridClient bot, UUID target)
{
return bot.Inventory.FetchItem(target, bot.Self.AgentID, 3000);
}
public static UUID GetAssetUUID(GridClient bot, UUID target)
{
InventoryItem A = getItemByInventoryUUID(bot, target);
if (A != null)
{
return A.AssetUUID;
}
return UUID.Zero;
}
public static InventoryBase FindFolder(GridClient bot, InventoryFolder current,UUID target)
{
List<InventoryBase> T = bot.Inventory.Store.GetContents(current);
InventoryBase Found = null;
foreach (InventoryBase R in T)
{
if(R.UUID == target)
{
Found = R;
break;
}
else
{
InventoryBase FoundSub = FindFolder(bot, (InventoryFolder)R, target);
if(FoundSub != null)
{
Found = FoundSub;
break;
}
}
}
return Found;
}
public static List<InventoryMapItem> getfolderinventory(GridClient bot, UUID folder)
{
List<InventoryMapItem> entrys = new List<InventoryMapItem>();
InventoryNode node = bot.Inventory.Store.GetNodeFor(folder);
InventoryMapItem mapitem = new InventoryMapItem();
mapitem.name = node.Data.Name;
mapitem.id = node.Data.UUID.ToString();
mapitem.typename = "Folder";
entrys.Add(mapitem);
List<InventoryBase> contents = bot.Inventory.FolderContents(folder, bot.Self.AgentID, true, true, InventorySortOrder.ByDate, 40 * 1000, true);
if (contents != null)
{
if (contents.Count > 0)
{
foreach (InventoryBase item in contents)
{
mapitem = new InventoryMapItem();
mapitem.name = item.Name;
mapitem.id = item.UUID.ToString();
mapitem.typename = item.GetType().Name;
entrys.Add(mapitem);
}
}
}
return entrys;
}
public static string MapFolderInventoryJson(GridClient bot,UUID folder)
{
return JsonConvert.SerializeObject(getfolderinventory(bot, folder));
}
public static KeyValuePair<string,string> MapFolderInventoryHumanReadable(GridClient bot, UUID folder)
{
StringBuilder output = new StringBuilder();
string foldername = "Unknown";
foreach(InventoryMapItem mitem in getfolderinventory(bot, folder))
{
if(mitem.typename == "Folder")
{
foldername = mitem.name;
}
output.Append("ID: ");
output.Append(mitem.id);
output.Append(" | ");
output.Append("Name: ");
output.Append(mitem.name);
output.Append(" | ");
output.Append("Type: ");
output.Append(mitem.typename);
output.Append("\n\r");
}
return new KeyValuePair<string,string>(foldername,output.ToString());
}
public static string MapFolderJson(GridClient bot,string targetFolder="root", bool subfolders = true)
{
if (bot.Inventory.Store != null)
{
if (bot.Inventory.Store.RootFolder != null)
{
InventoryFolder topLevel = null;
if (targetFolder == "root")
{
topLevel = bot.Inventory.Store.RootFolder;
}
else
{
if(UUID.TryParse(targetFolder, out UUID folderUUID) == true)
{
topLevel = new InventoryFolder(folderUUID);
}
}
if (topLevel != null)
{
return JsonConvert.SerializeObject(DoMapFolderJson(bot, topLevel, subfolders));
}
}
}
return null;
}
public static InventoryMapFolder DoMapFolderJson(GridClient bot, InventoryFolder folder, bool subfolders=true)
{
InventoryMapFolder ReplyFolder = new InventoryMapFolder();
ReplyFolder.id = folder.UUID.ToString();
ReplyFolder.name = folder.Name;
Dictionary<string, string> foldernode = new Dictionary<string, string>();
List<InventoryBase> T = bot.Inventory.Store.GetContents(folder);
foreach (InventoryBase R in T)
{
if (R.GetType() == typeof(InventoryFolder))
{
if (subfolders == true)
{
ReplyFolder.subfolders.Add(DoMapFolderJson(bot, (InventoryFolder)R, subfolders));
}
}
}
return ReplyFolder;
}
public static string MapFolderHumanReadable(GridClient bot)
{
StringBuilder B = new StringBuilder();
DoMapFolderHumanReadable(bot, 0, bot.Inventory.Store.RootFolder, B);
return B.ToString();
}
public static void DoMapFolderHumanReadable(GridClient bot,int level, InventoryFolder folder, StringBuilder reply)
{
List<InventoryBase> T = bot.Inventory.Store.GetContents(folder);
foreach (InventoryBase R in T)
{
if (R.GetType() == typeof(InventoryFolder))
{
reply.Append("" + Spaces(level) + "|- " + R.Name + " [" + R.UUID.ToString() + "]\n\r");
DoMapFolderHumanReadable(bot, level + 1, (InventoryFolder)R, reply);
}
}
}
public static string Spaces(int counter)
{
StringBuilder reply = new StringBuilder();
while (counter > 0)
{
reply.Append(" ");
counter--;
}
return reply.ToString();
}
}
}

View file

@ -0,0 +1,33 @@
using SecondBotEvents.Commands;
using System;
using System.Collections.Generic;
using System.Text;
namespace SecondBotEvents
{
public static class http_commands_helper
{
public static Dictionary<string, Type> getCommandModules()
{
Dictionary<string, Type> reply = new Dictionary<string, Type>();
reply.Add("animation", typeof(Animation));
reply.Add("avatars", typeof(Avatars));
reply.Add("chat", typeof(Chat));
reply.Add("core", typeof(Core));
reply.Add("dialogs", typeof(Dialogs));
reply.Add("discord", typeof(DiscordCommands));
reply.Add("estate", typeof(Estate));
reply.Add("friends", typeof(Friends));
reply.Add("funds", typeof(Funds));
reply.Add("group", typeof(Group));
reply.Add("info", typeof(Info));
reply.Add("inventory", typeof(Inventory));
reply.Add("movement", typeof(Movement));
reply.Add("notecard", typeof(Notecard));
reply.Add("parcel", typeof(ParcelCommands));
reply.Add("self", typeof(Self));
reply.Add("streamadmin", typeof(StreamAdmin));
return reply;
}
}
}

View file

@ -0,0 +1,98 @@
using OpenMetaverse;
using System;
using System.Collections.Generic;
using System.Text;
namespace SecondBotEvents
{
public static class parcel_static
{
public static bool set_parcel_music(GridClient bot, Parcel p, string url)
{
if (p.MusicURL != url)
{
p.MusicURL = url;
p.Update(bot.Network.CurrentSim, false);
}
return true;
}
public static void ParcelSetFlag(ParcelFlags F, Parcel p, bool set)
{
if (p.Flags.HasFlag(F))
{
p.Flags -= F;
}
if (set == true)
{
p.Flags |= F;
}
}
public static bool has_parcel_perm(Parcel P, GridClient bot)
{
bool has_perm = true;
if (P.OwnerID != bot.Self.AgentID)
{
if (P.IsGroupOwned)
{
if (bot.Groups.GroupName2KeyCache.ContainsKey(P.GroupID) == true)
{
bot.Groups.ActivateGroup(P.GroupID);
}
else
{
has_perm = false;
}
}
else
{
if(bot.Network.CurrentSim.IsEstateManager == false)
{
has_perm = false;
}
}
}
return has_perm;
}
public static string[] get_flag_names()
{
System.Type enumType = typeof(ParcelFlags);
List<string> flagnames = new List<string>(System.Enum.GetNames(enumType));
flagnames.Remove("None");
return flagnames.ToArray();
}
public static Dictionary<string, string> get_media_list()
{
Dictionary<string, string> flags = new Dictionary<string, string>();
flags.Add("MediaAutoScale", "Bool (True|False)");
flags.Add("MediaLoop", "Bool (True|False)");
flags.Add("MediaID", "UUID (Texture)");
flags.Add("MediaURL", "String");
flags.Add("MediaDesc", "String");
flags.Add("MediaHeight", "Int (256 to 1024)");
flags.Add("MediaWidth", "Int (256 to 1024)");
flags.Add("MediaType", "String [\"IMG-PNG\",\"IMG-JPG\",\"VID-MP4\",\"VID-AVI\" or \"Custom-MIME_TYPE_CODE\"]");
return flags;
}
public static Dictionary<string, ParcelFlags> get_flags_list()
{
Dictionary<string, ParcelFlags> flags = new Dictionary<string, ParcelFlags>();
Type enumType = typeof(ParcelFlags);
Array enumValues = Enum.GetValues(enumType);
Array enumNames = Enum.GetNames(enumType);
for (int i = 0; i < enumValues.Length; i++)
{
object value = enumValues.GetValue(i);
ParcelFlags realValue = (ParcelFlags)value;
if (realValue != 0)
{
string name = (string)enumNames.GetValue(i);
flags.Add(name, realValue);
}
}
return flags;
}
}
}