From 4d16a67a2bb7ac3266d86cce2a6cddbb4ff163bf Mon Sep 17 00:00:00 2001 From: ledocool Date: Thu, 3 Aug 2023 13:57:12 +0700 Subject: [PATCH] add basic create and list reminder api --- Budet-cho-bot/Budet-cho-bot.csproj | 5 + Budet-cho-bot/Helpers/Utility.cs | 58 ++++++ Budet-cho-bot/Models/CronRecord.cs | 15 ++ Budet-cho-bot/Modules/ExampleModule.cs | 179 ------------------- Budet-cho-bot/Modules/ReminderModule.cs | 77 ++++---- Budet-cho-bot/Repositories/CronRepository.cs | 33 ++++ Budet-cho-bot/Services/ReminderService.cs | 44 +++++ Budet-cho-bot/Utility.cs | 25 --- 8 files changed, 190 insertions(+), 246 deletions(-) create mode 100644 Budet-cho-bot/Helpers/Utility.cs create mode 100644 Budet-cho-bot/Models/CronRecord.cs delete mode 100644 Budet-cho-bot/Modules/ExampleModule.cs create mode 100644 Budet-cho-bot/Repositories/CronRepository.cs create mode 100644 Budet-cho-bot/Services/ReminderService.cs delete mode 100644 Budet-cho-bot/Utility.cs diff --git a/Budet-cho-bot/Budet-cho-bot.csproj b/Budet-cho-bot/Budet-cho-bot.csproj index c7bc99f..ce10f7f 100644 --- a/Budet-cho-bot/Budet-cho-bot.csproj +++ b/Budet-cho-bot/Budet-cho-bot.csproj @@ -20,4 +20,9 @@ + + + + + diff --git a/Budet-cho-bot/Helpers/Utility.cs b/Budet-cho-bot/Helpers/Utility.cs new file mode 100644 index 0000000..32ff826 --- /dev/null +++ b/Budet-cho-bot/Helpers/Utility.cs @@ -0,0 +1,58 @@ +using Budet_cho_bot.Models; +using Newtonsoft.Json.Linq; +using System.Linq; + +namespace Budet_cho_bot.Helpers; + +public static class Utility +{ + public static DateTime ParseDate(string message) + { + if (TimeSpan.TryParse(message, out var parsedTime) == false) + { + throw new ArgumentException("Хуйня твоё время"); + } + + var today = DateTime.Today; //returns xx/xx/xxxx 00:00:00, so adding time span would set correct daate + var date = today + parsedTime; + return date; + } + + public static TimeSpan DeserializeTime(string token) + { + return Newtonsoft.Json.JsonConvert.DeserializeObject(token); + } + + public static string PackTimetableIntoString(CronRecord[] timetable) + { + return string.Join(';', timetable.Select(x => + { + var unixRunTime = x.LastRun.ToUniversalTime().ToUnixTimeSeconds(); + var secondsPeriod = (int)(x.Period.TotalSeconds); + return $"{unixRunTime}:{secondsPeriod}:{x.IsOneShot}"; + })); + } + + public static CronRecord[] UnpackTimetableFromString(string timetable) + { + if(timetable == null) + { + return new CronRecord[0]; + } + + return timetable.Split(';').Select(x => + { + var record = x.Split(':'); //crude but whatever + var lastRun = DateTimeOffset.FromUnixTimeSeconds(long.Parse(record[0])); + var period = TimeSpan.FromSeconds(long.Parse(record[1])); + bool oneShot = bool.Parse(record[2]); + + return new CronRecord() + { + LastRun = lastRun, + Period = period, + IsOneShot = oneShot + }; + }).ToArray(); + } +} \ No newline at end of file diff --git a/Budet-cho-bot/Models/CronRecord.cs b/Budet-cho-bot/Models/CronRecord.cs new file mode 100644 index 0000000..8292fd1 --- /dev/null +++ b/Budet-cho-bot/Models/CronRecord.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Budet_cho_bot.Models +{ + public class CronRecord + { + public DateTimeOffset LastRun { set; get; } + public TimeSpan Period { set; get; } + public bool IsOneShot { set; get; } + } +} diff --git a/Budet-cho-bot/Modules/ExampleModule.cs b/Budet-cho-bot/Modules/ExampleModule.cs deleted file mode 100644 index 70bad4b..0000000 --- a/Budet-cho-bot/Modules/ExampleModule.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Discord.Commands; -using Discord.WebSocket; -using System; -using System.Threading.Tasks; -using Discord; - -namespace Discord_Bot -{ - public class ExampleModule : ModuleBase - { - // .say hello world -> hello world - [Command("say")] - [Summary("Echoes a message.")] - public async Task SayAsync([Remainder] [Summary("The text to echo")] string echo) - { - RemoveLastUserMessage(); - await ReplyAsync(echo); - } - private async void RemoveLastUserMessage() - { - var channel = Context.Channel as SocketTextChannel; - var items = await channel.GetMessagesAsync(1).FlattenAsync(); - await channel.DeleteMessagesAsync(items); - } - - [Command("hello")] // Command name. - [Summary("Say hello to the bot.")] // Command summary. - public async Task Hello() - => await ReplyAsync($"Hello there, **{Context.User.Username}**!"); - - [Command("pick")] - [Alias("choose")] // Aliases that will also trigger the command. - [Summary("Pick something.")] - public async Task Pick([Remainder]string message = "") - { - string[] options = message.Split(new string[] { " or " }, StringSplitOptions.RemoveEmptyEntries); - string selection = options[new Random().Next(options.Length)]; - - // ReplyAsync() is a shortcut for Context.Channel.SendMessageAsync() - await ReplyAsync($"I choose **{selection}**"); - } - - [Command("cookie")] - [Summary("Give someone a cookie.")] - public async Task Cookie(SocketGuildUser user) - { - if (Context.Message.Author.Id == user.Id) - await ReplyAsync($"{Context.User.Mention} doesn't have anyone to share a cookie with... :("); - else - await ReplyAsync($"{Context.User.Mention} shared a cookie with **{user.Username}** :cookie:"); - } - - [Command("amiadmin")] - [Summary("Check your administrator status")] - public async Task AmIAdmin() - { - if ((Context.User as SocketGuildUser).GuildPermissions.Administrator) - await ReplyAsync($"Yes, **{Context.User.Username}**, you're an admin!"); - else - await ReplyAsync($"No, **{Context.User.Username}**, you're **not** an admin!"); - } - - [Command("kick")] - [Summary("Kick a user from the server.")] - [RequireBotPermission(GuildPermission.KickMembers)] - [RequireUserPermission(GuildPermission.KickMembers)] - public async Task Kick(SocketGuildUser targetUser, [Remainder]string reason = "No reason provided.") - { - await targetUser.KickAsync(reason); - await ReplyAsync($"**{targetUser}** has been kicked. Bye bye :wave:"); - } - - [Command("ban")] - [Summary("Ban a user from the server")] - [RequireUserPermission(GuildPermission.BanMembers)] - [RequireBotPermission(GuildPermission.BanMembers)] - public async Task Ban(SocketGuildUser targetUser, [Remainder]string reason = "No reason provided.") - { - await Context.Guild.AddBanAsync(targetUser.Id, 0, reason); - await ReplyAsync($"**{targetUser}** has been banned. Bye bye :wave:"); - } - - [Command("unban")] - [Summary("Unban a user from the server")] - [RequireBotPermission(GuildPermission.BanMembers)] - [RequireUserPermission(GuildPermission.BanMembers)] - public async Task Unban(ulong targetUser) - { - await Context.Guild.RemoveBanAsync(targetUser); - await Context.Channel.SendMessageAsync($"The user has been unbanned :clap:"); - } - - [Command("purge")] - [Summary("Bulk deletes messages in chat")] - [RequireBotPermission(GuildPermission.ManageMessages)] - [RequireUserPermission(GuildPermission.ManageMessages)] - public async Task Purge(int delNumber) - { - var channel = Context.Channel as SocketTextChannel; - var items = await channel.GetMessagesAsync(delNumber + 1).FlattenAsync(); - await channel.DeleteMessagesAsync(items); - } - - [Command("reloadconfig")] - [Summary("Reloads the config and applies changes")] - [RequireOwner] // Require the bot owner to execute the command successfully. - public async Task ReloadConfig() - { - await Functions.SetBotStatusAsync(Context.Client); - await ReplyAsync("Reloaded!"); - } - - [Command("ping")] - [Summary("Show current latency.")] - public async Task Ping() - => await ReplyAsync($"Latency: {Context.Client.Latency} ms"); - - [Command("avatar")] - [Alias("getavatar")] - [Summary("Get a user's avatar.")] - public async Task GetAvatar([Remainder]SocketGuildUser user = null) - => await ReplyAsync($":frame_photo: **{(user ?? Context.User as SocketGuildUser).Username}**'s avatar\n{Functions.GetAvatarUrl(user)}"); - - - [Command("info")] - [Alias("server", "serverinfo")] - [Summary("Show server information.")] - [RequireBotPermission(GuildPermission.EmbedLinks)] // Require the bot the have the 'Embed Links' permissions to execute this command. - public async Task ServerEmbed() - { - double botPercentage = Math.Round(Context.Guild.Users.Count(x => x.IsBot) / Context.Guild.MemberCount * 100d, 2); - - EmbedBuilder embed = new EmbedBuilder() - .WithColor(0, 225, 225) - .WithDescription( - $"🏷️\n**Guild name:** {Context.Guild.Name}\n" + - $"**Guild ID:** {Context.Guild.Id}\n" + - $"**Created at:** {Context.Guild.CreatedAt:dd/M/yyyy}\n" + - $"**Owner:** {Context.Guild.Owner}\n\n" + - $"💬\n" + - $"**Users:** {Context.Guild.MemberCount - Context.Guild.Users.Count(x => x.IsBot)}\n" + - $"**Bots:** {Context.Guild.Users.Count(x => x.IsBot)} [ {botPercentage}% ]\n" + - $"**Channels:** {Context.Guild.Channels.Count}\n" + - $"**Roles:** {Context.Guild.Roles.Count}\n" + - $"**Emotes: ** {Context.Guild.Emotes.Count}\n\n" + - $"🌎 **Region:** {Context.Guild.VoiceRegionId}\n\n" + - $"🔒 **Security level:** {Context.Guild.VerificationLevel}") - .WithImageUrl(Context.Guild.IconUrl); - - await ReplyAsync($":information_source: Server info for **{Context.Guild.Name}**", embed: embed.Build()); - } - - [Command("role")] - [Alias("roleinfo")] - [Summary("Show information about a role.")] - public async Task RoleInfo([Remainder]SocketRole role) - { - // Just in case someone tries to be funny. - if (role.Id == Context.Guild.EveryoneRole.Id) - return; - - await ReplyAsync( - $":flower_playing_cards: **{role.Name}** information```ini" + - $"\n[Members] {role.Members.Count()}" + - $"\n[Role ID] {role.Id}" + - $"\n[Hoisted status] {role.IsHoisted}" + - $"\n[Created at] {role.CreatedAt:dd/M/yyyy}" + - $"\n[Hierarchy position] {role.Position}" + - $"\n[Color Hex] {role.Color}```"); - } - - // Please don't remove this command. I will appreciate it a lot <3 - [Command("source")] - [Alias("sourcecode", "src")] - [Summary("Link the source code used for this bot.")] - public async Task Source() - => await ReplyAsync($":heart: **{Context.Client.CurrentUser}** is based on this source code:\nhttps://github.com/VACEfron/Discord-Bot-Csharp"); - } -} diff --git a/Budet-cho-bot/Modules/ReminderModule.cs b/Budet-cho-bot/Modules/ReminderModule.cs index 9a8298e..7ab6e30 100644 --- a/Budet-cho-bot/Modules/ReminderModule.cs +++ b/Budet-cho-bot/Modules/ReminderModule.cs @@ -1,76 +1,69 @@ -using Discord.Commands; +using Budet_cho_bot.Helpers; +using Budet_cho_bot.Models; +using Budet_cho_bot.Services; +using Discord.Commands; using Discord.Commands.Builders; using Newtonsoft.Json.Linq; +using System.Text; namespace Discord_Bot { public class ReminderModule : ModuleBase { - private const long DAY_PERIOD = 24 * 60 * 60 * 1000; // 10 * 1000; // - private Timer reminderTimer; + private ReminderService _reminderService = new ReminderService(); protected override void OnModuleBuilding(CommandService commandService, ModuleBuilder builder) { base.OnModuleBuilding(commandService, builder); Console.WriteLine($"{DateTime.Now.TimeOfDay:hh\\:mm\\:ss} | hello world "); - ReloadTimers(); } - [Command("remindertime")] - private async Task SetReminderTime([Remainder]string message = "") + [Command("remind")] + public async Task CreateTask(string users, DateTime startsAt, string? repeat) { - SaveToConfig("reminderTime", Utility.ParseMessage(message)); - ReloadTimers(); - await ReplyAsync($"Заебись"); + bool oneShot = repeat == null; + var now = DateTime.Now; + TimeSpan rep; + TimeSpan.TryParse(repeat, out rep); + + var record = new CronRecord() + { + Period = oneShot ? startsAt - now : rep, + LastRun = oneShot ? now : startsAt, + IsOneShot = oneShot + }; + var created = await _reminderService.CreateTask(record); + await ReplyAsync($"Заебись: {created}"); } [Command("sequence")] - private async Task SetSequence([Remainder]string message = "") + private async Task SetSequence(string message = "") { - SaveToConfig("sequence", message); + //_reminderService.SaveToConfig("sequence", message); await ReplyAsync($"Заебись"); } [Command("users")] private async Task Users([Remainder]string message = "") { - SaveToConfig("users", message); + //_reminderService.SaveToConfig("users", message); await ReplyAsync($"Заебись"); } - - private void ReloadTimers() + + [Command("list")] + private async Task List() { - var time = Functions.GetConfigItem("reminderTime"); - if (time.Type == JTokenType.Boolean) - return; - - var nextReminder = Utility.ParseTime(time.ToString()); - var now = DateTime.Now; - - var span = (nextReminder - now).TotalMilliseconds; - if (span < 0) + var cron = await _reminderService.GetTasks(); + var sb = new StringBuilder(); + sb.AppendLine("Last Run | Period | Is one shot"); + sb.AppendLine("-------------------------------"); + foreach (var cr in cron) { - // skip to next day - nextReminder = nextReminder.AddDays(1); - span = (nextReminder - now).TotalMilliseconds; + sb.AppendLine($"{cr.LastRun.ToString("o")} | {cr.Period.ToString("c")} | {cr.IsOneShot}"); } - - reminderTimer = new Timer(SendReminder, "sd", (long)span, DAY_PERIOD); - } + sb.AppendLine("-------------------------------"); - private async void SendReminder(object? state) - { - //check sequence - - var users = Functions.GetConfigItem("users").ToString(); - await ReplyAsync($"{users}\nБудет чо?"); - } - - private void SaveToConfig(string key, string data) - { - var config = Functions.GetConfig(); - config[key] = data; - Functions.SaveConfig(config); + await ReplyAsync(sb.ToString()); } } } \ No newline at end of file diff --git a/Budet-cho-bot/Repositories/CronRepository.cs b/Budet-cho-bot/Repositories/CronRepository.cs new file mode 100644 index 0000000..bfc71d9 --- /dev/null +++ b/Budet-cho-bot/Repositories/CronRepository.cs @@ -0,0 +1,33 @@ +using Budet_cho_bot.Helpers; +using Discord_Bot; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Budet_cho_bot.Repositories +{ + public class CronRepository + { + public async Task SetCronTask(string taskType, string task) + { + var config = Functions.GetConfig(); + config[taskType] = task; + Functions.SaveConfig(config); + + return 1; + } + + public async Task GetCronTask(string taskType) + { + var config = Functions.GetConfig(); + if (config.TryGetValue(taskType, out var tasks)) + { + return tasks.ToString(); + } + + return null; + } + } +} diff --git a/Budet-cho-bot/Services/ReminderService.cs b/Budet-cho-bot/Services/ReminderService.cs new file mode 100644 index 0000000..01d10bb --- /dev/null +++ b/Budet-cho-bot/Services/ReminderService.cs @@ -0,0 +1,44 @@ +using Budet_cho_bot.Helpers; +using Budet_cho_bot.Models; +using Budet_cho_bot.Repositories; +using Discord; +using Discord_Bot; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Budet_cho_bot.Services +{ + internal class ReminderService + { + private readonly CronRepository _cronRepository = new CronRepository(); + private const long DAY_PERIOD = 24 * 60 * 60 * 1000; // 10 * 1000; // + private Timer reminderTimer; + + + public async void SendReminder(object? state) + { + var users = Functions.GetConfigItem("users").ToString(); + //await ReplyAsync($"{users}\nБудет чо?"); + } + + public async Task CreateTask(CronRecord record) + { + var cron = await _cronRepository.GetCronTask("reminder"); + var cronUnpacked = Utility.UnpackTimetableFromString(cron); + + var result = await _cronRepository.SetCronTask("reminder", Utility.PackTimetableIntoString(cronUnpacked.Append(record).ToArray())); + return result; + } + + public async Task GetTasks() + { + var cron = await _cronRepository.GetCronTask("reminder"); + var cronUnpacked = Utility.UnpackTimetableFromString(cron); + return cronUnpacked; + } + } +} diff --git a/Budet-cho-bot/Utility.cs b/Budet-cho-bot/Utility.cs deleted file mode 100644 index f039f46..0000000 --- a/Budet-cho-bot/Utility.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Newtonsoft.Json.Linq; - -namespace Discord_Bot; - -public static class Utility -{ - public static string ParseMessage(string message) - { - var time = message.Split(":"); - if (time.Length != 2) - { - throw new ArgumentException("Время неправильное. Хуйня твоё время"); - return null; - } - - var now = DateTime.Now; - var date = new DateTime(now.Year, now.Month, now.Day, int.Parse(time[0]), int.Parse(time[1]), 0); - return Newtonsoft.Json.JsonConvert.SerializeObject(date); - } - - public static DateTime ParseTime(string token) - { - return Newtonsoft.Json.JsonConvert.DeserializeObject(token); - } -} \ No newline at end of file