Skip to content

Commit

Permalink
hubot inside botbot
Browse files Browse the repository at this point in the history
  • Loading branch information
golf1052 committed Aug 18, 2019
1 parent efa5f0a commit 472b804
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 48 deletions.
132 changes: 84 additions & 48 deletions botbot/Client.cs
Expand Up @@ -89,6 +89,7 @@ static Client()
});

event EventHandler<SlackMessageEventArgs> MessageReceived;
event EventHandler<SlackRTMEventArgs> EventReceived;

// <channel, <user, timestamp>>
Dictionary<string, Dictionary<string, DateTime>> typings;
Expand All @@ -104,6 +105,8 @@ static Client()
NewReleasesGPMCommand newReleasesGPMCommand;

List<IMessageModule> messageModules;
List<IEventModule> eventModules;
HubotModule hubotModule;

HttpClient httpClient;

Expand All @@ -113,6 +116,7 @@ public Client(Settings settings, ILogger<Client> logger)
httpClient = new HttpClient();
webSocket = new ClientWebSocket();
MessageReceived += Client_MessageReceived;
EventReceived += Client_EventReceived;
slackCore = new SlackCore(settings.Token);
slackUsers = new List<SlackUser>();
typings = new Dictionary<string, Dictionary<string, DateTime>>();
Expand All @@ -124,6 +128,7 @@ public Client(Settings settings, ILogger<Client> logger)
newReleasesCommand = new NewReleasesCommand();
newReleasesGPMCommand = new NewReleasesGPMCommand();
messageModules = new List<IMessageModule>();
eventModules = new List<IEventModule>();
this.logger = logger;
}

Expand Down Expand Up @@ -153,9 +158,42 @@ public async Task Connect(Uri uri)
messageModules.Add(new ReactionsModule(slackCore, SendSlackMessage));
messageModules.Add(new NewReleasesModule());

eventModules.Add(new TypingModule(slackCore, SendMessage));
if (settings.HubotEnabled)
{
hubotModule = new HubotModule(slackCore, SendMessage);
await hubotModule.Init(settings.Token);
eventModules.Add(hubotModule);
}

foreach (var eventModule in eventModules)
{
RecurringModule recurring = eventModule.RegisterRecurring();
if (recurring != null)
{
Task t = Task.Run(async () =>
{
while (true)
{
try
{
await recurring.Func.Invoke();
}
catch (Exception ex)
{
logger.LogError(ex, "Error while running recurring module");
}
await Task.Delay(recurring.Interval);
}
});
}
}

//await soundcloud.Auth();
Task.Run(() => CheckTypings());
Task.Run(() => SendTypings(GetChannelIdByName(settings.TestingChannel)));
if (!string.IsNullOrEmpty(settings.TestingChannel))
{
Task.Run(() => SendTypings(GetChannelIdByName(settings.TestingChannel)));
}
//await SendSlackMessage(spotify.GetAuthUrl(), golf1052Channel);
Task.Run(() => CanAccessMongo());
Task.Run(() => CheckNewReleases());
Expand Down Expand Up @@ -274,35 +312,6 @@ public async Task SendTypings(string channel)
await Task.Delay(TimeSpan.FromMinutes(waitFor));
}
}

public async Task CheckTypings()
{
while (true)
{
// clean stale
foreach (var typing in typings)
{
List<string> typingUsers = typing.Value.Keys.ToList();
foreach (string user in typingUsers)
{
if (DateTime.UtcNow - typing.Value[user] >= TimeSpan.FromSeconds(3))
{
typing.Value.Remove(user);
}
}
}

// send on active
foreach (var typing in typings)
{
if (typing.Value.Count >= 4)
{
await SendTyping(typing.Key);
}
}
await Task.Delay(TimeSpan.FromSeconds(1));
}
}

public async Task Receive()
{
Expand Down Expand Up @@ -348,34 +357,25 @@ public async Task Receive()
string messageType = (string)o["type"];
if (messageType == "message")
{
hubotModule.Handle(messageType, o);
SlackMessageEventArgs newMessage = new SlackMessageEventArgs();
newMessage.Message = o;
MessageReceived(this, newMessage);
}
else if (messageType == "user_typing")
{
string channel = (string)o["channel"];
string user = (string)o["user"];
if (!typings.ContainsKey(channel))
{
typings.Add(channel, new Dictionary<string, DateTime>());
}
if (!typings[channel].ContainsKey(user))
{
typings[channel].Add(user, DateTime.UtcNow);
}
else
{
typings[channel][user] = DateTime.UtcNow;
}
}
else if (messageType == "user_change")
{
if (!string.IsNullOrEmpty(settings.StatusChannel))
{
await ProcessProfileChange(o);
}
}
else
{
SlackRTMEventArgs rtmEvent = new SlackRTMEventArgs();
rtmEvent.Type = messageType;
rtmEvent.Event = o;
EventReceived(this, rtmEvent);
}
}

pipe.Reader.AdvanceTo(buffer.Start, buffer.End);
Expand Down Expand Up @@ -410,6 +410,42 @@ private string GetString(ReadOnlySequence<byte> buffer)
});
}
}
private async void Client_EventReceived(object sender, SlackRTMEventArgs e)
{
List<Task> eventModuleTasks = new List<Task>();
foreach (var module in eventModules)
{
eventModuleTasks.Add(module.Handle(e.Type, e.Event));
}

bool allTasksDone = false;
while (!allTasksDone)
{
allTasksDone = true;
for (int i = 0; i < eventModuleTasks.Count; i++)
{
var task = eventModuleTasks[i];
if (task.IsCompletedSuccessfully)
{
await task;
eventModuleTasks.RemoveAt(i);
i--;
continue;
}
else if (task.IsFaulted)
{
logger.LogWarning($"task failed {task.Exception.Message}");
eventModuleTasks.RemoveAt(i);
i--;
continue;
}
else
{
allTasksDone = false;
}
}
}
}

private async void Client_MessageReceived(object sender, SlackMessageEventArgs e)
{
Expand Down
26 changes: 26 additions & 0 deletions botbot/Controllers/BotBotController.cs
Expand Up @@ -20,6 +20,7 @@ public class BotBotController : Controller
public static List<Task> clientTasks;
public static HttpClient httpClient;
public static StockCommand stockCommand;
public static string HubotWorkspace;

static BotBotController()
{
Expand All @@ -40,6 +41,10 @@ public static async Task StartClients(ILogger<Client> logger)
Client client = new Client(workspaceSettings, logger);
JObject connectionInfo = await client.GetConnectionInfo();
string teamId = (string)connectionInfo["team"]["id"];
if (workspaceSettings.HubotEnabled)
{
HubotWorkspace = teamId;
}
clients.Add(teamId, client);
clientTasks.Add(client.Connect(new Uri((string)connectionInfo["url"])));
}
Expand Down Expand Up @@ -76,6 +81,27 @@ public async void SlashCommand()
}
}

[HttpPost("/hubot")]
public async void HubotResponse([FromBody]JObject responseObject)
{
if ((string)responseObject["type"] == "message")
{
if (!string.IsNullOrEmpty(HubotWorkspace))
{
string text = (string)responseObject["text"];
string channel = (string)responseObject["channel"];
if (responseObject["thread_ts"] != null)
{
clients[HubotWorkspace].SendSlackMessage(text, channel, (string)responseObject["thread_ts"]);
}
else
{
clients[HubotWorkspace].SendSlackMessage(text, channel);
}
}
}
}

private JObject ProcessSlashCommand(RequestBody requestBody)
{
string[] splitText = requestBody.Text.Split(' ');
Expand Down
68 changes: 68 additions & 0 deletions botbot/Module/HubotModule.cs
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using golf1052.SlackAPI;
using Newtonsoft.Json.Linq;

namespace botbot.Module
{
public class HubotModule : SlackEventModule
{
private readonly HttpClient httpClient;
private readonly string baseUrl;

public HubotModule(SlackCore slackCore, Func<string, Task> SendMessage) : base(slackCore, SendMessage)
{
httpClient = new HttpClient();
baseUrl = $"http://localhost:{Secrets.HubotPortNumber}";
}

public override async Task Handle(string type, JObject e)
{
if (type == "hello")
{
}
else if (type == "goodbye")
{
}
else
{
JObject requestObject = new JObject();
requestObject["slack"] = e;
SendHubot("slack", requestObject);
}
}

public async Task Init(string token)
{
JObject requestObject = new JObject();
requestObject["token"] = token;
await SendHubot("init", requestObject);
}

private async Task SendHubot(string type, JObject requestObject)
{
requestObject["type"] = type;
try
{
HttpResponseMessage responseMessage = await httpClient.PostAsync(baseUrl, new StringContent(requestObject.ToString(), Encoding.UTF8, "application/json"));
if (!responseMessage.IsSuccessStatusCode)
{
// maybe log?
}
}
catch (Exception)
{
// if hubot is down don't really worry about it
}
}

public override RecurringModule RegisterRecurring()
{
return null;
}
}
}
15 changes: 15 additions & 0 deletions botbot/Module/IEventModule.cs
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace botbot.Module
{
public interface IEventModule
{
Task Handle(string type, JObject e);

RecurringModule RegisterRecurring();
}
}
20 changes: 20 additions & 0 deletions botbot/Module/RecurringModule.cs
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace botbot.Module
{
public class RecurringModule
{
public TimeSpan Interval { get; private set; }

public Func<Task> Func { get; private set; }

public RecurringModule(TimeSpan interval, Func<Task> func)
{
Interval = interval;
Func = func;
}
}
}
25 changes: 25 additions & 0 deletions botbot/Module/SlackEventModule.cs
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using golf1052.SlackAPI;
using Newtonsoft.Json.Linq;

namespace botbot.Module
{
public abstract class SlackEventModule : IEventModule
{
protected readonly SlackCore slackCore;
protected readonly Func<string, Task> SendMessageFunc;

public SlackEventModule(SlackCore slackCore, Func<string, Task> SendMessage)
{
this.slackCore = slackCore;
SendMessageFunc = SendMessage;
}

public abstract Task Handle(string type, JObject e);

public abstract RecurringModule RegisterRecurring();
}
}

0 comments on commit 472b804

Please sign in to comment.