diff --git a/JetHerald/Commands/CreateTopicCommand.cs b/JetHerald/Commands/CreateTopicCommand.cs index bd373ac..06b62c9 100644 --- a/JetHerald/Commands/CreateTopicCommand.cs +++ b/JetHerald/Commands/CreateTopicCommand.cs @@ -20,7 +20,6 @@ namespace JetHerald.Commands if (cmd.Parameters.Length < 1) return null; var msg = messageEventArgs.Message; - var chatid = msg.Chat.Id; if (msg.Chat.Type != ChatType.Private) return null; @@ -32,7 +31,7 @@ namespace JetHerald.Commands try { - var topic = await db.CreateTopic(msg.From.Id, "Telegram", name, descr); + var topic = await db.CreateTopic(NamespacedId.Telegram(msg.From.Id), name, descr); return $"created {topic.Name}\n" + $"readToken\n{topic.ReadToken}\n" + $"writeToken\n{topic.WriteToken}\n" + diff --git a/JetHerald/Commands/DeleteTopicCommand.cs b/JetHerald/Commands/DeleteTopicCommand.cs index dd9275c..6e87e78 100644 --- a/JetHerald/Commands/DeleteTopicCommand.cs +++ b/JetHerald/Commands/DeleteTopicCommand.cs @@ -18,7 +18,6 @@ namespace JetHerald.Commands if (cmd.Parameters.Length < 2) return null; var msg = messageEventArgs.Message; - var chatid = msg.Chat.Id; if (msg.Chat.Type != ChatType.Private) return null; @@ -26,8 +25,11 @@ namespace JetHerald.Commands string name = cmd.Parameters[0]; string adminToken = cmd.Parameters[1]; - var topic = await db.DeleteTopic(name, adminToken); - return $"deleted {name} and all its subscriptions"; + var changed = await db.DeleteTopic(name, adminToken); + if (changed > 0) + return ($"deleted {name} and all its subscriptions"); + else + return ($"invalid topic name or admin token"); } } } diff --git a/JetHerald/Commands/DiscordCommands.cs b/JetHerald/Commands/DiscordCommands.cs index 96901d1..91f9840 100644 --- a/JetHerald/Commands/DiscordCommands.cs +++ b/JetHerald/Commands/DiscordCommands.cs @@ -9,7 +9,7 @@ namespace JetHerald [ModuleLifespan(ModuleLifespan.Transient)] public class DiscordCommands : BaseCommandModule { - public Db db { private get; set; } + public Db Db { get; set; } [Command("createtopic")] [Description("Creates a topic.")] @@ -28,7 +28,7 @@ namespace JetHerald try { - var topic = await db.CreateTopic((long)ctx.User.Id, "Discord", name, description); + var topic = await Db.CreateTopic(NamespacedId.Discord(ctx.User.Id), name, description); await ctx.RespondAsync($"created {topic.Name}\n" + $"readToken\n{topic.ReadToken}\n" + $"writeToken\n{topic.WriteToken}\n" + @@ -51,7 +51,7 @@ namespace JetHerald string adminToken) { _ = ctx.TriggerTypingAsync(); - var changed = await db.DeleteTopic(name, adminToken); + var changed = await Db.DeleteTopic(name, adminToken); if (changed > 0) await ctx.RespondAsync($"deleted {name} and all its subscriptions"); else @@ -64,7 +64,7 @@ namespace JetHerald { _ = ctx.TriggerTypingAsync(); - var topics = await db.GetTopicsForChat((long)ctx.Channel.Id, "Discord"); + var topics = await Db.GetTopicsForChat(NamespacedId.Discord(ctx.Channel.Id)); await ctx.RespondAsync(topics.Any() ? "Topics:\n" + string.Join("\n", topics) @@ -81,17 +81,18 @@ namespace JetHerald { _ = ctx.TriggerTypingAsync(); - var topic = await db.GetTopic(token, (long)ctx.Channel.Id, "Discord"); + var chat = NamespacedId.Discord(ctx.Channel.Id); + var topic = await Db.GetTopicForSub(token, chat); if (topic == null) await ctx.RespondAsync("topic not found"); - else if (topic.ChatId == (long)ctx.Channel.Id) + else if (topic.Chat.HasValue && topic.Chat.Value == chat) await ctx.RespondAsync($"already subscribed to {topic.Name}"); else if (topic.ReadToken != token) await ctx.RespondAsync("token mismatch"); else { - await db.CreateSubscription(topic.TopicId, (long)ctx.Channel.Id, "Discord"); + await Db.CreateSubscription(topic.TopicId, chat); await ctx.RespondAsync($"subscribed to {topic.Name}"); } } @@ -106,7 +107,7 @@ namespace JetHerald { _ = ctx.TriggerTypingAsync(); - int affected = await db.RemoveSubscription(name, (long)ctx.Channel.Id, "Discord"); + int affected = await Db.RemoveSubscription(name, NamespacedId.Discord(ctx.Channel.Id)); if (affected >= 1) await ctx.RespondAsync($"unsubscribed from {name}"); else diff --git a/JetHerald/Commands/ListCommand.cs b/JetHerald/Commands/ListCommand.cs index 36af191..a6e29ff 100644 --- a/JetHerald/Commands/ListCommand.cs +++ b/JetHerald/Commands/ListCommand.cs @@ -17,7 +17,7 @@ namespace JetHerald { var msg = messageEventArgs.Message; var chatid = msg.Chat.Id; - var topics = await db.GetTopicsForChat(chatid, "Telegram"); + var topics = await db.GetTopicsForChat(NamespacedId.Telegram(chatid)); return topics.Any() ? "Topics:\n" + string.Join("\n", topics) diff --git a/JetHerald/Commands/SubscribeCommand.cs b/JetHerald/Commands/SubscribeCommand.cs index 87b4b57..f2f91c0 100644 --- a/JetHerald/Commands/SubscribeCommand.cs +++ b/JetHerald/Commands/SubscribeCommand.cs @@ -17,20 +17,20 @@ namespace JetHerald if (cmd.Parameters.Length < 1) return null; - var chatid = args.Message.Chat.Id; + var chat = NamespacedId.Telegram(args.Message.Chat.Id); var token = cmd.Parameters[0]; - var topic = await db.GetTopic(token, chatid, "Telegram"); + var topic = await db.GetTopicForSub(token, chat); if (topic == null) return "topic not found"; - else if (topic.ChatId == chatid) + else if (topic.Chat == chat) return $"already subscribed to {topic.Name}"; else if (topic.ReadToken != token) return "token mismatch"; else { - await db.CreateSubscription(topic.TopicId, chatid, "Telegram"); + await db.CreateSubscription(topic.TopicId, chat); return $"subscribed to {topic.Name}"; } } diff --git a/JetHerald/Commands/UnsubscribeCommand.cs b/JetHerald/Commands/UnsubscribeCommand.cs index 9126f4d..828243e 100644 --- a/JetHerald/Commands/UnsubscribeCommand.cs +++ b/JetHerald/Commands/UnsubscribeCommand.cs @@ -18,9 +18,10 @@ namespace JetHerald return null; var msg = messageEventArgs.Message; - var chatid = msg.Chat.Id; + var chat = NamespacedId.Telegram(msg.Chat.Id); + var topicName = cmd.Parameters[0]; - int affected = await db.RemoveSubscription(topicName, chatid, "Telegram"); + int affected = await db.RemoveSubscription(topicName, chat); if (affected >= 1) return $"unsubscribed from {topicName}"; else diff --git a/JetHerald/Db.cs b/JetHerald/Db.cs index bf62820..4d36d3a 100644 --- a/JetHerald/Db.cs +++ b/JetHerald/Db.cs @@ -13,8 +13,7 @@ namespace JetHerald public class Topic { public uint TopicId { get; set; } - public long CreatorId { get; set; } - public string CreatorService { get; set; } + public NamespacedId Creator { get; set; } public string Name { get; set; } public string Description { get; set; } public string ReadToken { get; set; } @@ -23,8 +22,7 @@ namespace JetHerald public DateTime? ExpiryTime { get; set; } public bool ExpiryMessageSent { get; set; } - public long? ChatId { get; set; } - public string Service { get; set; } + public NamespacedId? Chat { get; set; } public override string ToString() => Name == Description ? Name : $"{Name}: {Description}"; @@ -32,18 +30,11 @@ namespace JetHerald public class ExpiredTopicChat { - public long ChatId; - public string Service; + public NamespacedId Chat; public string Description; public DateTime ExpiryTime { get; set; } } - public class ChatData - { - public long ChatId; - public string Service; - } - public async Task DeleteTopic(string name, string adminToken) { using (var c = GetConnection()) @@ -66,81 +57,78 @@ namespace JetHerald new { name }); } - public async Task GetTopic(string token, long chatId, string service) + public async Task GetTopicForSub(string token, NamespacedId chat) { - using (var c = GetConnection()) - return await c.QuerySingleOrDefaultAsync( - " SELECT t.*, tc.ChatId " + - " FROM topic t " + - " LEFT JOIN topic_chat tc ON t.TopicId = tc.TopicId AND tc.ChatId = @chatId AND tc.Service = @service " + - " WHERE ReadToken = @token", - new { token, chatId, service }); + using var c = GetConnection(); + return await c.QuerySingleOrDefaultAsync( + " SELECT t.*, tc.Chat " + + " FROM topic t " + + " LEFT JOIN topic_chat tc ON t.TopicId = tc.TopicId AND tc.Chat = @chat " + + " WHERE ReadToken = @token", + new { token, chat }); } - public async Task CreateTopic(long userId, string service, string name, string descr) + public async Task CreateTopic(NamespacedId user, string name, string descr) { var t = new Topic { - CreatorId = userId, + Creator = user, Name = name, Description = descr, ReadToken = TokenHelper.GetToken(), WriteToken = TokenHelper.GetToken(), - AdminToken = TokenHelper.GetToken(), - Service = service + AdminToken = TokenHelper.GetToken() }; - using (var c = GetConnection()) - { - return await c.QuerySingleOrDefaultAsync( + using var c = GetConnection(); + return await c.QuerySingleOrDefaultAsync( " INSERT INTO herald.topic " + - " ( CreatorId, Name, Description, ReadToken, WriteToken, AdminToken, Service) " + + " ( Creator, Name, Description, ReadToken, WriteToken, AdminToken) " + " VALUES " + - " (@CreatorId, @Name, @Description, @ReadToken, @WriteToken, @AdminToken, @Service); " + + " (@Creator, @Name, @Description, @ReadToken, @WriteToken, @AdminToken); " + " SELECT * FROM topic WHERE TopicId = LAST_INSERT_ID(); ", - t); - } + t); } - public async Task> GetChatIdsForTopic(uint topicid) + public async Task> GetChatsForTopic(uint topicid) { - using (var c = GetConnection()) - return await c.QueryAsync( - " SELECT ChatId, Service" + - " FROM topic_chat" + - " WHERE TopicId = @topicid", - new { topicid }); + using var c = GetConnection(); + return await c.QueryAsync( + " SELECT Chat " + + " FROM topic_chat " + + " WHERE TopicId = @topicid", + new { topicid }); } - public async Task> GetTopicsForChat(long chatid, string service) + public async Task> GetTopicsForChat(NamespacedId chat) { - using (var c = GetConnection()) - return await c.QueryAsync( - " SELECT t.*" + - " FROM topic_chat ct" + - " JOIN topic t on t.TopicId = ct.TopicId" + - " WHERE ct.ChatId = @chatid AND ct.Service = @service", - new { chatid, service }); + using var c = GetConnection(); + return await c.QueryAsync( + " SELECT t.*" + + " FROM topic_chat ct" + + " JOIN topic t on t.TopicId = ct.TopicId" + + " WHERE ct.Chat = @chat", + new { chat }); } - public async Task CreateSubscription(uint topicId, long chatId, string service) + public async Task CreateSubscription(uint topicId, NamespacedId chat) { - using (var c = GetConnection()) - await c.ExecuteAsync( - " INSERT INTO topic_chat" + - " (ChatId, TopicId, Service)" + - " VALUES" + - " (@chatId, @topicId, @service)", - new { topicId, chatId, service }); + using var c = GetConnection(); + await c.ExecuteAsync( + " INSERT INTO topic_chat" + + " (Chat, TopicId)" + + " VALUES" + + " (@chat, @topicId)", + new { topicId, chat }); } - public async Task RemoveSubscription(string topicName, long chatId, string service) + public async Task RemoveSubscription(string topicName, NamespacedId chat) { - using (var c = GetConnection()) - return await c.ExecuteAsync( - " DELETE tc " + - " FROM topic_chat tc" + - " JOIN topic t ON tc.TopicId = t.TopicId " + - " WHERE t.Name = @topicName AND tc.ChatId = @chatId AND tc.Service = @service;", - new { topicName, chatId, service }); + using var c = GetConnection(); + return await c.ExecuteAsync( + " DELETE tc " + + " FROM topic_chat tc" + + " JOIN topic t ON tc.TopicId = t.TopicId " + + " WHERE t.Name = @topicName AND tc.Chat = @chat;", + new { topicName, chat }); } public Task AddExpiry(string topicName, int addedTime) @@ -154,17 +142,6 @@ namespace JetHerald new { topicName, addedTime }); } - public Task DisableExpiry(string name, string adminToken) - { - using var c = GetConnection(); - return c.ExecuteAsync( - " UPDATE topic" + - " SET ExpiryTime = NULL," + - " ExpiryMessageSent = 0" + - " WHERE Name = @name AND AdminToken = @adminToken", - new { name, adminToken }); - } - public Task> GetExpiredTopics(CancellationToken token = default) { using var c = GetConnection(); diff --git a/JetHerald/JetHeraldBot.Discord.cs b/JetHerald/JetHeraldBot.Discord.cs index 0d06974..9687581 100644 --- a/JetHerald/JetHeraldBot.Discord.cs +++ b/JetHerald/JetHeraldBot.Discord.cs @@ -30,9 +30,10 @@ namespace JetHerald } - private async Task SendMessageToDiscordChannel(long chatId, string formatted) + async Task SendMessageToDiscordChannel(NamespacedId chat, string formatted) { - await DiscordBot.SendMessageAsync(await DiscordBot.GetChannelAsync((ulong)chatId), formatted); + var id = ulong.Parse(chat.Id); + await DiscordBot.SendMessageAsync(await DiscordBot.GetChannelAsync(id), formatted); } } } \ No newline at end of file diff --git a/JetHerald/JetHeraldBot.cs b/JetHerald/JetHeraldBot.cs index 3ffd96d..914e17d 100644 --- a/JetHerald/JetHeraldBot.cs +++ b/JetHerald/JetHeraldBot.cs @@ -92,13 +92,7 @@ namespace JetHerald try { foreach (var chatSent in await Db.GetExpiredTopics(token)) - { - var formatted = $"!{chatSent.Description}!:\nTimeout expired at {chatSent.ExpiryTime}"; - if (chatSent.Service == "Telegram") - await TelegramBot.SendTextMessageAsync(chatSent.ChatId, formatted, cancellationToken: token); - else if (chatSent.Service == "Discord") - await SendMessageToDiscordChannel(chatSent.ChatId, formatted); - } + await SendMessageImpl(chatSent.Chat, $"!{chatSent.Description}!:\nTimeout expired at {chatSent.ExpiryTime}"); await Db.MarkExpiredTopics(token); } @@ -109,30 +103,33 @@ namespace JetHerald } } - public async Task HeartbeatSent(Db.Topic topic) + public Task HeartbeatSent(Db.Topic topic) + => BroadcastMessageImpl(topic, $"!{topic.Description}!:\nA heartbeat has been sent."); + + public Task PublishMessage(Db.Topic topic, string message) + => BroadcastMessageImpl(topic, $"|{topic.Description}|:\n{message}"); + + async Task BroadcastMessageImpl(Db.Topic topic, string formatted) { - var chatIds = await Db.GetChatIdsForTopic(topic.TopicId); - var formatted = $"!{topic.Description}!:\nA heartbeat has been sent."; + var chatIds = await Db.GetChatsForTopic(topic.TopicId); foreach (var c in chatIds) - { - if (c.Service == "Telegram") - await TelegramBot.SendTextMessageAsync(c.ChatId, formatted); - else if (c.Service == "Discord") - await SendMessageToDiscordChannel(c.ChatId, formatted); - } + await SendMessageImpl(c, formatted); } - public async Task PublishMessage(Db.Topic topic, string message) + async Task SendMessageImpl(NamespacedId chat, string formatted) { - var chatIds = await Db.GetChatIdsForTopic(topic.TopicId); - var formatted = $"|{topic.Description}|:\n{message}"; - foreach (var c in chatIds) + try { - if (c.Service == "Telegram") - await TelegramBot.SendTextMessageAsync(c.ChatId, formatted); - else if (c.Service == "Discord") - await SendMessageToDiscordChannel(c.ChatId, formatted); + if (chat.Namespace == "telegram") + { + await TelegramBot.SendTextMessageAsync(long.Parse(chat.Id), formatted); + } + else if (chat.Namespace == "discord") + { + await SendMessageToDiscordChannel(chat, formatted); + } } + catch (Exception e) { Log.LogError(e, $"Error while sending message \"{formatted}\" to {chat}"); } } async void BotOnMessageReceived(object sender, MessageEventArgs messageEventArgs)