Document ChatProviders

This commit is contained in:
Cyberboss
2017-10-20 15:22:59 -04:00
parent 2fba289804
commit 8dc17d009f
3 changed files with 141 additions and 35 deletions
+13 -13
View File
@@ -4,7 +4,7 @@ using TGServiceInterface;
namespace TGServerService.ChatProviders
{
/// <summary>
/// Callback for the chat provider recieving a message
/// Callback for the chat provider recieving a <paramref name="message"/>
/// </summary>
/// <param name="ChatProvider">The chat provider the message came from</param>
/// <param name="speaker">The username of the speaker</param>
@@ -20,16 +20,16 @@ namespace TGServerService.ChatProviders
interface ITGChatProvider : IDisposable
{
/// <summary>
/// Sets info for the provider
/// Sets <paramref name="info"/> for the provider
/// </summary>
/// <param name="info">The info to set</param>
/// <param name="info">The <see cref="ChatSetupInfo"/> to set</param>
/// <returns>null on success, error message on failure</returns>
string SetProviderInfo(ChatSetupInfo info);
/// <summary>
/// Gets the info of the provider
/// </summary>
/// <returns>The info for the chat provider</returns>
/// <returns>The <see cref="ChatSetupInfo"/> for the chat provider</returns>
ChatSetupInfo ProviderInfo();
/// <summary>
@@ -40,18 +40,18 @@ namespace TGServerService.ChatProviders
/// <summary>
/// Connects the chat provider if it's enabled
/// </summary>
/// <returns>null on success, error message on failure</returns>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string Connect();
/// <summary>
/// Forces a reconnection of the chat provider if it's enabled
/// </summary>
/// <returns>null on success, error message on failure</returns>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string Reconnect();
/// <summary>
/// Checks if the chat provider is connected
/// </summary>
/// <returns>true if the provider is connected, false otherwise</returns>
/// <returns><see langword="true"/> if the provider is connected, <see langword="false"/> otherwise</returns>
bool Connected();
/// <summary>
@@ -60,18 +60,18 @@ namespace TGServerService.ChatProviders
void Disconnect();
/// <summary>
/// Send a message to a channel
/// Send a <paramref name="message"/> to a <paramref name="channel"/>
/// </summary>
/// <param name="message">The message to send</param>
/// <param name="channel">The channel to send to</param>
/// <returns>null on success, error message on failure</returns>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string SendMessageDirect(string message, string channel);
/// <summary>
/// Broadcast a message to appropriate channels based on the message type
/// Broadcast a <paramref name="message"/> to appropriate channels based on the message type
/// </summary>
/// <param name="msg">The message to send</param>
/// <param name="mt">The message type</param>
void SendMessage(string msg, MessageType mt);
/// <param name="message">The message to send</param>
/// <param name="mt">The <see cref="MessageType"/></param>
void SendMessage(string message, MessageType mt);
}
}
@@ -8,25 +8,50 @@ using TGServiceInterface;
namespace TGServerService.ChatProviders
{
/// <summary>
/// <see cref="ITGChatProvider"/> for Discord: https://discordapp.com/
/// </summary>
class DiscordChatProvider : ITGChatProvider
{
/// <inheritdoc />
public event OnChatMessage OnChatMessage;
/// <summary>
/// The Discord API client
/// </summary>
DiscordSocketClient client;
/// <summary>
/// The setup info for the provider
/// </summary>
DiscordSetupInfo DiscordConfig;
/// <summary>
/// Used for multithreading safety
/// </summary>
object DiscordLock = new object();
/// <summary>
/// An <see cref="IDictionary{TKey, TValue}"/> of internal identifers => <see cref="ISocketMessageChannel"/>s we have seen
/// </summary>
IDictionary<ulong, ISocketMessageChannel> SeenPrivateChannels = new Dictionary<ulong, ISocketMessageChannel>();
/// <summary>
/// Construct a <see cref="DiscordChatProvider"/>
/// </summary>
/// <param name="info">The <see cref="ChatSetupInfo"/></param>
public DiscordChatProvider(ChatSetupInfo info)
{
Init(info);
}
/// <inheritdoc />
public ChatSetupInfo ProviderInfo()
{
return DiscordConfig;
}
/// <summary>
/// Sets up the Discord API <see cref="client"/> and <see cref="DiscordConfig"/>
/// </summary>
/// <param name="info">The <see cref="ChatSetupInfo"/> to init <see cref="DiscordConfig"/> with</param>
void Init(ChatSetupInfo info)
{
DiscordConfig = new DiscordSetupInfo(info);
@@ -34,17 +59,27 @@ namespace TGServerService.ChatProviders
client.MessageReceived += Client_MessageReceived;
}
private bool CheckAdmin(SocketUser u)
/// <summary>
/// Checks if a <paramref name="user"/> is considered a chat admin
/// </summary>
/// <param name="user">The sender of a message</param>
/// <returns><see langword="true"/> if <paramref name="user"/> is a chat admin, <see langword="false"/> otherwise</returns>
private bool CheckAdmin(SocketUser user)
{
if (!DiscordConfig.AdminsAreSpecial)
return DiscordConfig.AdminList.Contains(u.Id.ToString());
if(u is SocketGuildUser sgu)
return DiscordConfig.AdminList.Contains(user.Id.ToString());
if(user is SocketGuildUser sgu)
foreach (var I in sgu.Roles)
if (DiscordConfig.AdminList.Contains(I.Id.ToString()))
return true;
return false;
}
/// <summary>
/// Called when a channel the bot is in recieves a message or the bot is PM'd directly
/// </summary>
/// <param name="e">The event arguments</param>
/// <returns>The task to run when this occurs</returns>
private async Task Client_MessageReceived(SocketMessage e)
{
await Task.Run(() =>
@@ -81,6 +116,7 @@ namespace TGServerService.ChatProviders
});
}
/// <inheritdoc />
public string Connect()
{
try
@@ -102,6 +138,7 @@ namespace TGServerService.ChatProviders
}
}
/// <inheritdoc />
public bool Connected()
{
lock (DiscordLock)
@@ -110,6 +147,7 @@ namespace TGServerService.ChatProviders
}
}
/// <inheritdoc />
public void Disconnect()
{
try
@@ -126,12 +164,14 @@ namespace TGServerService.ChatProviders
catch { }
}
/// <inheritdoc />
public string Reconnect()
{
Disconnect();
return Connect();
}
/// <inheritdoc />
public void SendMessage(string msg, MessageType mt)
{
if (!Connected())
@@ -158,6 +198,7 @@ namespace TGServerService.ChatProviders
}
}
/// <inheritdoc />
public string SendMessageDirect(string message, string channelname)
{
if (!Connected())
@@ -185,6 +226,10 @@ namespace TGServerService.ChatProviders
return e.ToString();
}
}
/// <summary>
/// Shutsdown and disposes <see cref="client"/>
/// </summary>
void DisconnectAndDispose()
{
try
@@ -198,6 +243,7 @@ namespace TGServerService.ChatProviders
client.Dispose();
}
/// <inheritdoc />
public string SetProviderInfo(ChatSetupInfo info)
{
try
@@ -228,8 +274,15 @@ namespace TGServerService.ChatProviders
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
/// <summary>
/// To detect redundant <see cref="Dispose()"/> calls
/// </summary>
private bool disposedValue = false;
/// <summary>
/// Implements the <see cref="IDisposable"/> pattern
/// </summary>
/// <param name="disposing"><see langword="true"/> if <see cref="Dispose()"/> was called manually, <see langword="false"/> if it was from the finalizer</param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
@@ -252,8 +305,9 @@ namespace TGServerService.ChatProviders
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
/// <summary>
/// Implements the <see cref="IDisposable"/> pattern
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
@@ -4,25 +4,44 @@ using System.Threading;
using TGServiceInterface;
using Meebey.SmartIrc4net;
namespace TGServerService.ChatProviders
{
/// <summary>
/// <see cref="ITGChatProvider"/> for internet relay chat
/// </summary>
class IRCChatProvider : ITGChatProvider
{
/// <summary>
/// Header used to mark that a channel is actually a query message
/// </summary>
const string PrivateMessageMarker = "---PRIVATE-MSG---";
/// <summary>
/// The irc client
/// </summary>
IrcFeatures irc;
/// <summary>
/// Used for multithreading safety
/// </summary>
object IRCLock = new object();
/// <summary>
/// The setup info for the provider
/// </summary>
IRCSetupInfo IRCConfig;
/// <inheritdoc />
public event OnChatMessage OnChatMessage;
/// <inheritdoc />
public ChatSetupInfo ProviderInfo()
{
return IRCConfig;
}
/// <summary>
/// Construct a <see cref="IRCChatProvider"/>
/// </summary>
/// <param name="info">The <see cref="ChatSetupInfo"/></param>
public IRCChatProvider(ChatSetupInfo info)
{
IRCConfig = new IRCSetupInfo(info);
@@ -41,8 +60,8 @@ namespace TGServerService.ChatProviders
irc.OnChannelMessage += Irc_OnChannelMessage;
irc.OnQueryMessage += Irc_OnQueryMessage;
}
//public api
/// <inheritdoc />
public string SendMessageDirect(string message, string channel)
{
try
@@ -64,6 +83,7 @@ namespace TGServerService.ChatProviders
}
}
/// <inheritdoc />
public string SetProviderInfo(ChatSetupInfo info)
{
var convertedInfo = (IRCSetupInfo)info;
@@ -84,6 +104,11 @@ namespace TGServerService.ChatProviders
return null;
}
/// <summary>
/// Checks if a message is considered sent from a chat admin
/// </summary>
/// <param name="e">The <see cref="IrcMessageData"/></param>
/// <returns><see langword="true"/> if <paramref name="e"/> was sent by a chat admin, <see langword="false"/> otherwise</returns>
private bool CheckAdmin(IrcMessageData e)
{
if (IRCConfig.AdminsAreSpecial)
@@ -122,12 +147,21 @@ namespace TGServerService.ChatProviders
return false;
}
//private message
/// <summary>
/// Called when the bot recieves a query message
/// </summary>
/// <param name="sender">The sender of the event (usually <see cref="irc"/>)</param>
/// <param name="e">The <see cref="IrcEventArgs"/></param>
private void Irc_OnQueryMessage(object sender, IrcEventArgs e)
{
OnChatMessage(this, e.Data.Nick, e.Data.Nick + PrivateMessageMarker, e.Data.Message, CheckAdmin(e.Data), true);
}
/// <summary>
/// Called when a channel the bot is in recieves a message
/// </summary>
/// <param name="sender">The sender of the event (usually <see cref="irc"/>)</param>
/// <param name="e">The <see cref="IrcEventArgs"/></param>
private void Irc_OnChannelMessage(object sender, IrcEventArgs e)
{
var formattedMessage = e.Data.Message.Trim();
@@ -144,7 +178,10 @@ namespace TGServerService.ChatProviders
OnChatMessage(this, e.Data.Nick, e.Data.Channel, formattedMessage, CheckAdmin(e.Data), IRCConfig.AdminChannels.Contains(e.Data.Channel.ToLower()));
}
//Joins configured channels
/// <summary>
/// Joins all channels specified in <see cref="IRCConfig"/>
/// </summary>
void JoinChannels()
{
var hs = new HashSet<string>(); //for unique inserts
@@ -165,7 +202,10 @@ namespace TGServerService.ChatProviders
foreach (var I in hs)
irc.RfcJoin(I);
}
//runs the login command
/// <summary>
/// Sends a login query to <see cref="IRCSetupInfo.AuthTarget"/> with message <see cref="IRCSetupInfo.AuthMessage"/>
/// </summary>
void Login()
{
lock (IRCLock)
@@ -174,7 +214,7 @@ namespace TGServerService.ChatProviders
irc.SendMessage(SendType.Message, IRCConfig.AuthTarget, IRCConfig.AuthMessage);
}
}
//public api
/// <inheritdoc />
public string Connect()
{
if (Connected() || !IRCConfig.Enabled)
@@ -213,7 +253,9 @@ namespace TGServerService.ChatProviders
}
}
//This is the thread that listens for irc messages
/// <summary>
/// Runs the <see cref="irc"/> listener in a safe loop
/// </summary>
void IRCListen()
{
while (irc != null && Connected())
@@ -224,14 +266,14 @@ namespace TGServerService.ChatProviders
catch { }
}
//public api
/// <inheritdoc />
public string Reconnect()
{
Disconnect();
return Connect();
}
//public api
/// <inheritdoc />
public void Disconnect()
{
try
@@ -250,7 +292,7 @@ namespace TGServerService.ChatProviders
Service.WriteError("IRC failed QnD: " + e.ToString(), EventID.ChatDisconnectFail);
}
}
//public api
/// <inheritdoc />
public bool Connected()
{
lock (IRCLock)
@@ -258,7 +300,7 @@ namespace TGServerService.ChatProviders
return irc != null && irc.IsConnected;
}
}
//public api
/// <inheritdoc />
public void SendMessage(string message, MessageType mt)
{
if (!Connected())
@@ -279,8 +321,15 @@ namespace TGServerService.ChatProviders
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
/// <summary>
/// To detect redundant <see cref="Dispose()"/> calls
/// </summary>
private bool disposedValue = false;
/// <summary>
/// Implements the <see cref="IDisposable"/> pattern
/// </summary>
/// <param name="disposing"><see langword="true"/> if <see cref="Dispose()"/> was called manually, <see langword="false"/> if it was from the finalizer</param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
@@ -306,6 +355,9 @@ namespace TGServerService.ChatProviders
// }
// This code added to correctly implement the disposable pattern.
/// <summary>
/// Implements the <see cref="IDisposable"/> pattern
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.