Commit db1d9f80 authored by Robert Rudman's avatar Robert Rudman

Throttle chat messages. Burnaki follows must include a tag so we can display deleted questions

parent b460130a
Pipeline #41073796 passed with stages
in 4 minutes and 45 seconds
......@@ -8,6 +8,8 @@ namespace Rodgort.Data.Tables
public int RoomId { get; set; }
public int BurnakiId { get; set; }
public string Tag { get; set; }
public DateTime FollowStarted { get; set; }
public DateTime? FollowEnded { get; set; }
}
......
using Microsoft.EntityFrameworkCore.Migrations;
namespace Rodgort.Migrations
{
public partial class BurnakiFollowsRequireATag : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Tag",
table: "BurnakiFollows",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Tag",
table: "BurnakiFollows");
}
}
}
......@@ -32,6 +32,8 @@ namespace Rodgort.Migrations
b.Property<int>("RoomId");
b.Property<string>("Tag");
b.HasKey("Id");
b.ToTable("BurnakiFollows");
......
......@@ -53,13 +53,14 @@ namespace Rodgort.Services
var burnakiFollows = context.BurnakiFollows.Where(bf => !bf.FollowEnded.HasValue).ToList();
foreach (var burnakiFollow in burnakiFollows)
FollowInRoom(burnakiFollow.RoomId, burnakiFollow.BurnakiId, burnakiFollow.FollowStarted, dateService, cancellationToken);
FollowInRoom(burnakiFollow.RoomId, burnakiFollow.BurnakiId, burnakiFollow.FollowStarted, burnakiFollow.Tag, dateService, cancellationToken);
var events = chatClient.SubscribeToEvents(ChatSite.StackOverflow, Headquarters);
await events.FirstAsync();
chatClient.SendMessage(ChatSite.StackOverflow, Headquarters, "o/");
_logger.LogInformation("Successfully joined headquarters");
await events
.ReplyAlive()
.Pinged()
.SameRoomOnly()
.Where(r => r.ChatEventDetails.UserId == RobUserId)
......@@ -74,7 +75,7 @@ namespace Rodgort.Services
});
}
private async Task FollowInRoom(int roomId, int followingUserId, DateTime fromTime, DateService dateService, CancellationToken cancellationToken)
private async Task FollowInRoom(int roomId, int followingUserId, DateTime fromTime, string followingTag, DateService dateService, CancellationToken cancellationToken)
{
await RunWithLogging(async () =>
{
......@@ -88,6 +89,7 @@ namespace Rodgort.Services
_logger.LogInformation($"Successfully joined room {roomId}");
chatClient.SendMessage(ChatSite.StackOverflow, Headquarters, $"I just joined {roomId}");
await events
.ReplyAlive()
.OnlyMessages()
.SameRoomOnly()
.Where(r => r.ChatEventDetails.UserId == followingUserId)
......@@ -185,7 +187,7 @@ namespace Rodgort.Services
AddIfNew(new DbUserAction
{
UserActionTypeId = DbUserActionType.UNKNOWN_DELETION,
Tag = null,
Tag = followingTag,
PostId = questionId,
Time = dateService.UtcNow,
SiteUserId = -1
......@@ -261,11 +263,17 @@ namespace Rodgort.Services
private async Task ProcessFollow(ChatClient chatClient, ChatEvent chatEvent, DateService dateService, CancellationToken cancellationToken, List<string> args)
{
if (args.Count != 3)
return;
if (!int.TryParse(args[0], out var burnakiUserId))
return;
if (!int.TryParse(args[1], out var roomId))
return;
var followingTag = args[2];
if (string.IsNullOrWhiteSpace(followingTag))
return;
var innerContext = _serviceProvider.GetRequiredService<RodgortContext>();
if (innerContext.BurnakiFollows.Any(bf => bf.BurnakiId == burnakiUserId && bf.RoomId == roomId && !bf.FollowEnded.HasValue))
{
......@@ -277,13 +285,14 @@ namespace Rodgort.Services
{
BurnakiId = burnakiUserId,
RoomId = roomId,
Tag = followingTag,
FollowStarted = dateService.UtcNow
});
innerContext.SaveChanges();
await chatClient.SendMessage(ChatSite.StackOverflow, chatEvent.RoomDetails.RoomId, $"Okay, following {burnakiUserId} in {roomId}");
FollowInRoom(roomId, burnakiUserId, DateTime.UtcNow, dateService, cancellationToken);
FollowInRoom(roomId, burnakiUserId, DateTime.UtcNow, followingTag, dateService, cancellationToken);
}
}
......@@ -314,7 +323,7 @@ namespace Rodgort.Services
var allFollows = innerContext.BurnakiFollows.Where(bf => !bf.FollowEnded.HasValue);
if (allFollows.Any())
{
var followMessage = $"I'm following: {string.Join(", ", allFollows.Select(f => $"{f.BurnakiId} in {f.RoomId}"))}";
var followMessage = $"I'm following: {string.Join(", ", allFollows.Select(f => $"{f.BurnakiId} in {f.RoomId} for tag {f.Tag}"))}";
await chatClient.SendMessage(ChatSite.StackOverflow, chatEvent.RoomDetails.RoomId, $":{chatEvent.ChatEventDetails.MessageId} {followMessage}");
}
else
......
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Linq;
using System.Threading.Tasks;
using Hangfire;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Rodgort.ApiUtilities;
using Rodgort.Data;
using Rodgort.Data.Tables;
using Rodgort.Utilities;
using StackExchangeApi;
using StackExchangeApi.Responses;
namespace Rodgort.Services
{
......@@ -23,7 +14,6 @@ namespace Rodgort.Services
private readonly DbContextOptions<RodgortContext> _dbContextOptions;
private readonly ApiClient _apiClient;
public UserDisplayNameService(DbContextOptions<RodgortContext> dbContextOptions, ApiClient apiClient)
{
_dbContextOptions = dbContextOptions;
......
using System;
using StackExchangeChat;
using StackExchangeChat.Utilities;
namespace Rodgort.Utilities
{
public static class ChatEventUtils
{
public static IObservable<ChatEvent> ReplyAlive(this IObservable<ChatEvent> observable)
{
observable
.OnlyMessages()
.SameRoomOnly()
.Subscribe(async o =>
{
var lookingFor = $"@{o.RoomDetails.MyUserName} alive";
if (string.Equals(o.ChatEventDetails.Content, lookingFor))
{
await o.ChatClient.SendMessage(o.RoomDetails.ChatSite, o.RoomDetails.RoomId, $":{o.ChatEventDetails.MessageId} yep");
}
});
return observable;
}
}
}
......@@ -16,6 +16,9 @@ namespace StackExchangeChat
private readonly SiteAuthenticator _siteAuthenticator;
private readonly HttpClientWithHandler _httpClient;
public static object TaskLocker = new object();
public static Task ExecutingTask = Task.CompletedTask;
public ChatClient(SiteAuthenticator siteAuthenticator, HttpClientWithHandler httpClient)
{
_siteAuthenticator = siteAuthenticator;
......@@ -24,15 +27,37 @@ namespace StackExchangeChat
public async Task SendMessage(ChatSite chatSite, int roomId, string message)
{
var fkey = (await _siteAuthenticator.GetRoomDetails(chatSite, roomId)).FKey;
await _siteAuthenticator.AuthenticateClient(_httpClient, chatSite);
await _httpClient.PostAsync($"https://{chatSite.ChatDomain}/chats/{roomId}/messages/new",
new FormUrlEncodedContent(
new Dictionary<string, string>
{
{"text", message},
{"fkey", fkey}
}));
Task nextMessageTask;
lock (TaskLocker)
{
if (ExecutingTask.IsFaulted)
ExecutingTask = Task.CompletedTask;
nextMessageTask = SendMessageInternal(ExecutingTask);
ExecutingTask = PostProcess(nextMessageTask);
}
await nextMessageTask;
async Task PostProcess(Task executingTask)
{
await executingTask;
await Task.Delay(TimeSpan.FromSeconds(5));
}
async Task SendMessageInternal(Task previousTask)
{
await previousTask;
var fkey = (await _siteAuthenticator.GetRoomDetails(chatSite, roomId)).FKey;
await _siteAuthenticator.AuthenticateClient(_httpClient, chatSite);
await _httpClient.PostAsync($"https://{chatSite.ChatDomain}/chats/{roomId}/messages/new",
new FormUrlEncodedContent(
new Dictionary<string, string>
{
{"text", message},
{"fkey", fkey}
}));
}
}
public IObservable<ChatEvent> SubscribeToEvents(ChatSite chatSite, int roomId)
......@@ -77,7 +102,8 @@ namespace StackExchangeChat
var chatEvent = new ChatEvent
{
RoomDetails = roomDetails,
ChatEventDetails = @event
ChatEventDetails = @event,
ChatClient = this
};
observer.OnNext(chatEvent);
}
......@@ -90,9 +116,10 @@ namespace StackExchangeChat
ChatEventDetails = new ChatEventDetails
{
ChatEventType = ChatEventType.ChatJoined,
RoomId = roomId,
RoomId = roomId
},
RoomDetails = roomDetails
RoomDetails = roomDetails,
ChatClient = this
});
return Disposable.Create(() =>
......
......@@ -4,6 +4,8 @@ namespace StackExchangeChat
{
public class ChatEvent
{
public ChatClient ChatClient { get; set; }
public ChatEventDetails ChatEventDetails { get; set; }
public RoomDetails RoomDetails { get; set; }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment