From 0d93936bc84a3c72183c7cb6278aacef1484c331 Mon Sep 17 00:00:00 2001 From: Noi Date: Tue, 19 May 2020 19:06:02 -0700 Subject: [PATCH] Make corresponding changes from VB-based project --- BackgroundServices/StaleDataCleaner.cs | 84 +++++++++++++++++++------- Data/GuildUserSettings.cs | 1 + DiscordBots.md | 18 ------ License.txt | 2 +- Readme.md | 35 +++++++++-- UserInterface/HelpInfoCommands.cs | 8 ++- 6 files changed, 100 insertions(+), 48 deletions(-) delete mode 100644 DiscordBots.md diff --git a/BackgroundServices/StaleDataCleaner.cs b/BackgroundServices/StaleDataCleaner.cs index 49a05ca..142db55 100644 --- a/BackgroundServices/StaleDataCleaner.cs +++ b/BackgroundServices/StaleDataCleaner.cs @@ -1,5 +1,7 @@ using BirthdayBot.Data; +using NpgsqlTypes; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace BirthdayBot.BackgroundServices @@ -13,33 +15,73 @@ namespace BirthdayBot.BackgroundServices public override async Task OnTick() { - using var db = await BotInstance.Config.DatabaseSettings.OpenConnectionAsync(); - - // Update only for all guilds the bot has cached - using (var c = db.CreateCommand()) + // Build a list of all values to update + var updateList = new Dictionary>(); + foreach (var gi in BotInstance.GuildCache) { - c.CommandText = $"update {GuildStateInformation.BackingTable} set last_seen = now() " - + "where guild_id = @Gid"; - var updateGuild = c.Parameters.Add("@Gid", NpgsqlTypes.NpgsqlDbType.Bigint); - c.Prepare(); + var existingUsers = new List(); + updateList[gi.Key] = existingUsers; - var list = new List(BotInstance.GuildCache.Keys); - foreach (var id in list) - { - updateGuild.Value = (long)id; - c.ExecuteNonQuery(); - } + var guild = BotInstance.DiscordClient.GetGuild(gi.Key); + if (guild == null) continue; // Have cache without being in guild. Unlikely, but... + + // Get IDs of cached users which are currently in the guild + var cachedUserIds = from cu in gi.Value.Users select cu.UserId; + var guildUserIds = from gu in guild.Users select gu.Id; + var existingCachedIds = cachedUserIds.Intersect(guildUserIds); } - // Delete all old values - expecte referencing tables to have 'on delete cascade' - using (var t = db.BeginTransaction()) + using (var db = await BotInstance.Config.DatabaseSettings.OpenConnectionAsync()) { - using (var c = db.CreateCommand()) + // Prepare to update a lot of last-seen values + var cUpdateGuild = db.CreateCommand(); + cUpdateGuild.CommandText = $"update {GuildStateInformation.BackingTable} set last_seen = now() " + + "where guild_id = @Gid"; + var pUpdateG = cUpdateGuild.Parameters.Add("@Gid", NpgsqlDbType.Bigint); + cUpdateGuild.Prepare(); + + var cUpdateGuildUser = db.CreateCommand(); + cUpdateGuildUser.CommandText = $"update {GuildUserSettings.BackingTable} set last_seen = now() " + + "where guild_id = @Gid and user_id = @Uid"; + var pUpdateGU_g = cUpdateGuildUser.Parameters.Add("@Gid", NpgsqlDbType.Bigint); + var pUpdateGU_u = cUpdateGuild.Parameters.Add("@Uid", NpgsqlDbType.Bigint); + cUpdateGuildUser.Prepare(); + + // Do actual updates + foreach (var item in updateList) { - // Delete data for guilds not seen in 2 weeks - c.CommandText = $"delete from {GuildUserSettings.BackingTable} where (now() - interval '14 days') > last_seen"; - var r = c.ExecuteNonQuery(); - if (r != 0) Log($"Removed {r} stale guild(s)."); + var guild = item.Key; + var userlist = item.Value; + + pUpdateG.Value = (long)guild; + cUpdateGuild.ExecuteNonQuery(); + + pUpdateGU_g.Value = (long)guild; + foreach (var userid in userlist) + { + pUpdateGU_u.Value = (long)userid; + cUpdateGuildUser.ExecuteNonQuery(); + } + } + + // Delete all old values - expects referencing tables to have 'on delete cascade' + using (var t = db.BeginTransaction()) + { + using (var c = db.CreateCommand()) + { + // Delete data for guilds not seen in 4 weeks + c.CommandText = $"delete from {GuildStateInformation.BackingTable} where (now() - interval '28 days') > last_seen"; + var r = c.ExecuteNonQuery(); + if (r != 0) Log($"Removed {r} stale guild(s)."); + } + using (var c = db.CreateCommand()) + { + // Delete data for users not seen in 8 weeks + c.CommandText = $"delete from {GuildUserSettings.BackingTable} where (now() - interval '56 days') > last_seen"; + var r = c.ExecuteNonQuery(); + if (r != 0) Log($"Removed {r} stale user(s)."); + } + t.Commit(); } } } diff --git a/Data/GuildUserSettings.cs b/Data/GuildUserSettings.cs index d604b0e..f923fb8 100644 --- a/Data/GuildUserSettings.cs +++ b/Data/GuildUserSettings.cs @@ -131,6 +131,7 @@ namespace BirthdayBot.Data + "birth_month integer not null, " + "birth_day integer not null, " + "time_zone text null, " + + "last_seen timestamptz not null default NOW(), " + "PRIMARY KEY (guild_id, user_id)" + ")"; c.ExecuteNonQuery(); diff --git a/DiscordBots.md b/DiscordBots.md deleted file mode 100644 index 0699323..0000000 --- a/DiscordBots.md +++ /dev/null @@ -1,18 +0,0 @@ -## Recognize birthdays in your Discord community! - -Birthday Bot is a simple, single-purpose bot. It will set a role on your users for the duration of their birthdays and, if desired, can announce a message in a channel of your choosing. Server owners can further specify a default time zone, with individual users also setting their own to ensure everyone's birthdays are recognized precisely on time. - -#### Getting started -* Invite the bot. Be mindful that it requires role setting permissions. -* Create a dedicated birthday role to be used only by the bot. Ensure the new role is placed beneath the bot's own role. - * **Do not use an existing role!** This bot assumes exclusive control over it. Users that have the role but are not having a birthday *will* be removed from it! -* Instruct the bot to use the role: `bb.config role (role name)` - -#### Other tips -* Set the birthday announcement channel: `bb.config channel (channel)` -* Set a default time zone: `bb.config zone (time zone)` - * Use the command `bb.help-tzdata` for information on how to specify time zones. -* Customize the announcement message: See `bb.help-message` for more information. - -#### Note -Birthday information is not shared between servers. This is *by design*, as some people prefer to share their birthdays with select groups of people but keep it obscured from other communities. \ No newline at end of file diff --git a/License.txt b/License.txt index 84d0671..aa0e00a 100644 --- a/License.txt +++ b/License.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2019 Noi, a.k.a. Noiiko +Copyright (c) 2018-2020 Noi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Readme.md b/Readme.md index 25a6f21..4466e3d 100644 --- a/Readme.md +++ b/Readme.md @@ -1,10 +1,35 @@ # Birthday Bot -Recognize birthdays in your Discord community! - * Info: https://discord.bots.gg/bots/470673087671566366 -* Invite: https://discordapp.com/oauth2/authorize?client_id=470673087671566366&scope=bot&permissions=268435456 -This bot will automatically add a role to users during their birthdays. If desired, it will also announce birthdays in a channel of your choosing. Time zones are supported per-server and per-user to ensure that birthdays and events are recognized at appropriate times. +## Recognize birthdays in your Discord community! -For more information, see the `DiscordBots.md` file. \ No newline at end of file +Birthday Bot is a simple, single-purpose bot. It will set a role on your users for the duration of their birthdays and, if desired, can announce a message in a channel of your choosing. Server owners can further specify a default time zone, with individual users also setting their own to ensure everyone's birthdays are recognized precisely on time. + +#### Getting started +This bot requires a small amount of initial set-up before it's ready for use. To quickly get started, ensure that you: +* Create a dedicated birthday role to be used only by the bot. Ensure the new role is placed beneath the bot's own role. + * **Do not use an existing role!** This bot assumes exclusive control over it. Users that have the role but are not having a birthday *will* be removed from it! +* Instruct the bot to use the role: `bb.config role (role name)` +At this point, you may also wish to do the following optional steps: +* Set the birthday announcement channel: `bb.config channel (channel)` +* Set a default time zone: `bb.config zone (time zone)` + * Use the command `bb.help-tzdata` for information on how to specify time zones. +* Customize the announcement message: See `bb.help-message` for more information. + +#### Support the bot +Birthday Bot is and shall remain free. I have no plans to hide new or existing features behind pay-only, premium features. This is an independent hobby project and all costs associated with it come out of my pocket. + +This bot has had a far greater response than I've ever expected, and at this point I find it difficult to pay for the server it runs on as its resource needs grow. I would greatly appreciate if you consider pitching in a little bit to cover my recurring costs by checking out my Patreon link: https://www.patreon.com/noibots. + +#### Support, Privacy and Security +The support server for my bots can be accessed via the given link: https://discord.gg/JCRyFk7. Further information in setting up the bot can be found within it, as well as a small group of volunteers who are willing to answer questions. + +This bot collects and stores only information necessary for its operation, including user, server, and role IDs. This data is not stored indefinitely, and is removed after some period of time depending on if the bot has been removed from a respective server or its users have been absent for a long enough time. + +Information is not shared between servers. This is *by design*, for those preferring to share their birthdays with only certain communities instead of automatically sharing it to all of them. Users must enter their birthday information onto every server they share with the bot for the servers they wish for it to be known in. + +Any questions and concerns regarding data and security may be sent to the bot author via the support server or GitHub. + +#### Image credit +The icon used by this bot is from Flaticon, found at: https://www.flaticon.com/free-icon/birthday-cake_168532 \ No newline at end of file diff --git a/UserInterface/HelpInfoCommands.cs b/UserInterface/HelpInfoCommands.cs index e719efd..b10f9e6 100644 --- a/UserInterface/HelpInfoCommands.cs +++ b/UserInterface/HelpInfoCommands.cs @@ -164,9 +164,11 @@ namespace BirthdayBot.UserInterface IconUrl = Discord.CurrentUser.GetAvatarUrl() }, // TODO this message needs an overhaul - Description = "Suggestions and feedback are always welcome. Please refer to the listing on Discord Bots " - + "(discord.bots.gg) for information on reaching my personal server. I may not be available often, but I am happy to " - + "respond to feedback in due time." // TODO update this string + Description = "For more information regarding support, data retention, privacy, and other details, please refer to: " + + "https://github.com/NoiTheCat/BirthdayBot/blob/master/Readme.md" + "\n\n" + + "This bot is provided for free, without any intention to add premium, pay-only features. " + + "If you find this bot helpful, please consider contributing towards my operating costs " + + "via Patreon: https://www.patreon.com/noibots." }.AddField(new EmbedFieldBuilder() { Name = "Statistics",