diff --git a/Catalogger.Backend/Api/GuildsController.Remove.cs b/Catalogger.Backend/Api/GuildsController.Remove.cs
new file mode 100644
index 0000000..5f4d2f6
--- /dev/null
+++ b/Catalogger.Backend/Api/GuildsController.Remove.cs
@@ -0,0 +1,110 @@
+// Copyright (C) 2021-present sam (starshines.gay)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+using System.Net;
+using Catalogger.Backend.Api.Middleware;
+using Catalogger.Backend.Bot;
+using Catalogger.Backend.Database.Queries;
+using Catalogger.Backend.Extensions;
+using Catalogger.Backend.Services;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+using Remora.Discord.Extensions.Embeds;
+
+namespace Catalogger.Backend.Api;
+
+public partial class GuildsController
+{
+ [HttpPost("leave")]
+ public async Task LeaveGuildAsync(string id, [FromBody] LeaveGuildRequest req)
+ {
+ var (guildId, guild) = await ParseGuildAsync(id);
+ if (guild.Name != req.Name)
+ {
+ throw new ApiError(
+ HttpStatusCode.BadRequest,
+ ErrorCode.BadRequest,
+ "You must spell the server name correctly."
+ );
+ }
+
+ var guildConfig = await db.GetGuildAsync(guildId.Value);
+ var logChannelId =
+ webhookExecutor.GetLogChannel(guildConfig, LogChannelType.GuildUpdate)
+ ?? webhookExecutor.GetLogChannel(guildConfig, LogChannelType.GuildMemberRemove);
+ if (logChannelId != null)
+ {
+ var currentUser = await discordRequestService.GetMeAsync(CurrentToken);
+
+ var embed = new EmbedBuilder()
+ .WithTitle("Catalogger is leaving this server")
+ .WithDescription(
+ $"A moderator in this server ({currentUser.Tag}, <@{currentUser.Id}>) requested that Catalogger leave. "
+ + "All data related to this server will be deleted."
+ )
+ .WithColour(DiscordUtils.Red)
+ .WithCurrentTimestamp()
+ .Build()
+ .GetOrThrow();
+
+ await webhookExecutor.SendLogAsync(logChannelId.Value, [embed], []);
+ }
+ else
+ {
+ _logger.Information(
+ "Can't send guild {GuildId} a heads up when leaving as neither the guild update nor member remove events are logged",
+ guildId
+ );
+ }
+
+ var leaveRes = await userApi.LeaveGuildAsync(guildId);
+ if (!leaveRes.IsSuccess)
+ {
+ _logger.Error("Couldn't leave guild {GuildId}: {Error}", guildId, leaveRes.Error);
+ throw new ApiError(
+ HttpStatusCode.InternalServerError,
+ ErrorCode.InternalServerError,
+ "There was an error making Catalogger leave the server."
+ );
+ }
+
+ await using var tx = await db.Database.BeginTransactionAsync();
+ var inviteCount = await db
+ .Invites.Where(i => i.GuildId == guildId.Value)
+ .ExecuteDeleteAsync();
+ var watchlistCount = await db
+ .Watchlists.Where(w => w.GuildId == guildId.Value)
+ .ExecuteDeleteAsync();
+ var messageCount = await db
+ .Messages.Where(m => m.GuildId == guildId.Value)
+ .ExecuteDeleteAsync();
+ await db.Guilds.Where(g => g.Id == guildId.Value).ExecuteDeleteAsync();
+ await tx.CommitAsync();
+
+ _logger.Information(
+ "Deleted {InviteCount} invites, {WatchlistCount} watchlist entries, and {MessageCount} messages for guild {GuildId}",
+ inviteCount,
+ watchlistCount,
+ messageCount,
+ guildId
+ );
+
+ _logger.Information("Left guild {GuildId} and removed all data for it", guildId);
+
+ return NoContent();
+ }
+
+ public record LeaveGuildRequest(string Name);
+}
diff --git a/Catalogger.Backend/Api/GuildsController.cs b/Catalogger.Backend/Api/GuildsController.cs
index 9032a67..11acc42 100644
--- a/Catalogger.Backend/Api/GuildsController.cs
+++ b/Catalogger.Backend/Api/GuildsController.cs
@@ -20,9 +20,11 @@ using Catalogger.Backend.Cache.InMemoryCache;
using Catalogger.Backend.Database;
using Catalogger.Backend.Database.Queries;
using Catalogger.Backend.Database.Redis;
+using Catalogger.Backend.Services;
using Microsoft.AspNetCore.Mvc;
using Remora.Discord.API;
using Remora.Discord.API.Abstractions.Objects;
+using Remora.Discord.API.Abstractions.Rest;
using Remora.Rest.Core;
namespace Catalogger.Backend.Api;
@@ -30,13 +32,17 @@ namespace Catalogger.Backend.Api;
[Route("/api/guilds/{id}")]
public partial class GuildsController(
Config config,
+ ILogger logger,
DatabaseContext db,
ChannelCache channelCache,
- RedisService redisService,
IMemberCache memberCache,
- DiscordRequestService discordRequestService
+ DiscordRequestService discordRequestService,
+ IDiscordRestUserAPI userApi,
+ WebhookExecutorService webhookExecutor
) : ApiControllerBase
{
+ private readonly ILogger _logger = logger.ForContext();
+
private async Task<(Snowflake GuildId, Guild Guild)> ParseGuildAsync(string id)
{
var guilds = await discordRequestService.GetGuildsAsync(CurrentToken);
diff --git a/Catalogger.Backend/Api/MetaController.cs b/Catalogger.Backend/Api/MetaController.cs
index 5037c9a..f5d9fe6 100644
--- a/Catalogger.Backend/Api/MetaController.cs
+++ b/Catalogger.Backend/Api/MetaController.cs
@@ -20,13 +20,25 @@ using Microsoft.AspNetCore.Mvc;
namespace Catalogger.Backend.Api;
[Route("/api")]
-public class MetaController(GuildCache guildCache, DiscordRequestService discordRequestService)
- : ApiControllerBase
+public class MetaController(
+ Config config,
+ GuildCache guildCache,
+ DiscordRequestService discordRequestService
+) : ApiControllerBase
{
[HttpGet("meta")]
public IActionResult GetMeta()
{
- return Ok(new MetaResponse(Guilds: (int)CataloggerMetrics.GuildsCached.Value));
+ var inviteUrl =
+ $"https://discord.com/oauth2/authorize?client_id={config.Discord.ApplicationId}"
+ + "&permissions=537250993&scope=bot+applications.commands";
+
+ return Ok(
+ new MetaResponse(
+ Guilds: (int)CataloggerMetrics.GuildsCached.Value,
+ InviteUrl: inviteUrl
+ )
+ );
}
[HttpGet("current-user")]
@@ -48,7 +60,7 @@ public class MetaController(GuildCache guildCache, DiscordRequestService discord
);
}
- private record MetaResponse(int Guilds);
+ private record MetaResponse(int Guilds, string InviteUrl);
private record CurrentUserResponse(ApiUser User, IEnumerable Guilds);
}
diff --git a/Catalogger.Backend/config.example.ini b/Catalogger.Backend/config.example.ini
index 77c6f9b..6a41d72 100644
--- a/Catalogger.Backend/config.example.ini
+++ b/Catalogger.Backend/config.example.ini
@@ -1,14 +1,35 @@
[Logging]
+# The minimum level that will be logged. Possible options are: Debug, Information, Warning, Error, Fatal
LogEventLevel = Debug
+# Whether to log SQL queries. This is extremely verbose.
LogQueries = false
+# URL for a Seq instance to log to. Generally not needed if you're self-hosting.
+SeqLogUrl = http://localhost:5341
+# Whether to enable Prometheus metrics. If disabled, Catalogger will update metrics manually every so often.
+EnableMetrics = false
[Database]
Url = Host=localhost;Database=postgres;Username=postgres;Password=postgres
+# URL for a Redis-compatible database. Not required; Catalogger will fall back to in-memory caching if it's not set.
Redis = localhost:6379
-EncryptionKey = changeMe!FNmZbotJnAAJ7grWHDluCoKIwj6NcUagKE= # base64 key
+# The key used to encrypt message data. This should be base64;
+# you can use `openssl rand -base64 32` on the command line to generate a key.
+EncryptionKey = changeMe!FNmZbotJnAAJ7grWHDluCoKIwj6NcUagKE=
[Discord]
ApplicationId =
Token =
-CommandsGuildId =
-SyncCommands = true
\ No newline at end of file
+# If you only want to sync commands with a single server, uncomment the line below and put its ID.
+# CommandsGuildId =
+# If disabled, commands will not be synced with Discord. This generally shouldn't be turned off.
+SyncCommands = true
+# The channel where guild join/leave logs are sent. The bot needs to have permission to manage webhooks in this channel.
+GuildLogId = 803961980758261800
+# This is not necessary when not using the dashboard.
+# ClientSecret =
+
+# Dashboard related settings. You shouldn't need to change these.
+[Web]
+Host = localhost
+Port = 5005
+BaseUrl = https://catalogger.localhost
diff --git a/Catalogger.Frontend/package.json b/Catalogger.Frontend/package.json
index a86936f..114f4b8 100644
--- a/Catalogger.Frontend/package.json
+++ b/Catalogger.Frontend/package.json
@@ -30,7 +30,8 @@
"svelte-check": "^4.0.0",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
- "vite": "^5.0.3"
+ "vite": "^5.0.3",
+ "vite-plugin-markdown": "^2.2.0"
},
"type": "module"
}
diff --git a/Catalogger.Frontend/src/lib/components/Navbar.svelte b/Catalogger.Frontend/src/lib/components/Navbar.svelte
index 4d786cd..767accb 100644
--- a/Catalogger.Frontend/src/lib/components/Navbar.svelte
+++ b/Catalogger.Frontend/src/lib/components/Navbar.svelte
@@ -30,7 +30,7 @@
};
-
+Catalogger (isOpen = !isOpen)} />
diff --git a/Catalogger.Frontend/src/routes/+layout.svelte b/Catalogger.Frontend/src/routes/+layout.svelte
index 4070c7e..aae3f1a 100644
--- a/Catalogger.Frontend/src/routes/+layout.svelte
+++ b/Catalogger.Frontend/src/routes/+layout.svelte
@@ -8,16 +8,30 @@
export let data: LayoutData;
-
-
-
- Visit kit.svelte.dev to read the documentation
+ Catalogger is a logging bot for Discord that integrates with PluralKit's message proxying.
+
+
+ It adds extra information to edited and deleted messages: system ID, member
+ ID, and the linked account. It also ignores messages that trigger proxies, so
+ your deleted message logs will be free of duplicate messages.
+ It also shows system information for new members joining your server,
+ and can notify you if an account linked to a banned system joins your server.
+ Which invite a member used is also logged.
+{/if}
diff --git a/Catalogger.Frontend/src/routes/about/+layout.svelte b/Catalogger.Frontend/src/routes/about/+layout.svelte
new file mode 100644
index 0000000..4fa864c
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/+layout.svelte
@@ -0,0 +1 @@
+
diff --git a/Catalogger.Frontend/src/routes/about/+layout.ts b/Catalogger.Frontend/src/routes/about/+layout.ts
new file mode 100644
index 0000000..189f71e
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/+layout.ts
@@ -0,0 +1 @@
+export const prerender = true;
diff --git a/Catalogger.Frontend/src/routes/about/contact.md b/Catalogger.Frontend/src/routes/about/contact.md
new file mode 100644
index 0000000..38c6d30
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/contact.md
@@ -0,0 +1,5 @@
+# Contact
+
+The best way to contact us is through [the support server](https://discord.gg/b5jjJ96Jbv).
+
+If you would rather use email, please send an email to `catalogger [at] starshines [dot] gay`.
diff --git a/Catalogger.Frontend/src/routes/about/contact/+page.svelte b/Catalogger.Frontend/src/routes/about/contact/+page.svelte
new file mode 100644
index 0000000..a2a9650
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/contact/+page.svelte
@@ -0,0 +1,9 @@
+
+
+
+ Catalogger - Contact
+
+
+{@html html}
\ No newline at end of file
diff --git a/Catalogger.Frontend/src/routes/about/privacy.md b/Catalogger.Frontend/src/routes/about/privacy.md
new file mode 100644
index 0000000..2ef81d6
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/privacy.md
@@ -0,0 +1,34 @@
+# Privacy policy
+
+We're not lawyers and never will be, and we don't want to write a document no one can (or wants to) read.
+That being said, this bot handles sensitive information, so here's a list of what the bot collects.
+
+By using Catalogger's commands, or by participating in a server where it's logging events,
+you consent to have your data processed by the bot.
+
+This is the data Catalogger collects:
+
+- Messages, in servers where edited and/or deleted message logging is enabled.
+- Server-specific settings: which channels to log to, and which channels to ignore.
+
+You can opt out of your messages being collected by leaving servers the bot is in.
+Because this data is stored for moderation purposes, you cannot opt out while still staying in those servers.
+
+Messages are stored encrypted in the database, and automatically deleted after fifteen days.
+This retention time may be lowered in the future.
+
+This is the data Catalogger fetches from Discord, and is stored while the bot is running:
+
+- User information: IDs, usernames, and avatars.
+- Member information: Nicknames and roles.
+- Server information: all channels and all roles in a server.
+- Webhooks used for logging.
+
+Additionally, the dashboard collects the following data:
+
+- The servers a logged-in user is in, to check their permissions and provide a list of all servers they can manage.
+
+To clear your server's data, use the "Delete all data" page on the dashboard.
+Catalogger will automatically leave your server after confirmation.
+
+Note that that command won't remove any data from database backups. [Contact us](/about/contact) if you want those wiped too.
diff --git a/Catalogger.Frontend/src/routes/about/privacy/+page.svelte b/Catalogger.Frontend/src/routes/about/privacy/+page.svelte
new file mode 100644
index 0000000..05e85c6
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/privacy/+page.svelte
@@ -0,0 +1,9 @@
+
+
+
+ Privacy policy - Catalogger
+
+
+{@html html}
diff --git a/Catalogger.Frontend/src/routes/about/tos.md b/Catalogger.Frontend/src/routes/about/tos.md
new file mode 100644
index 0000000..77a36dd
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/tos.md
@@ -0,0 +1,22 @@
+# Terms of Service
+
+By using the Catalogger Discord bot (henceforth referred to as “the bot”) or the Catalogger dashboard (henceforth referred to as “the website”), you accept and agree to abide by the terms and provisions of this agreement.
+
+In addition, when using the bot’s services, or those of the website, you shall be subject to any posted guidelines or rules applicable to such services, which may be posted and modified from time to time.
+
+All such guidelines or rules are hereby incorporated by reference into the Terms Of Service (henceforth referred to as ‘the ToS’).
+
+ANY USE OF THE BOT OR THE WEBSITE CONSTITUTES AGREEMENT TO THESE TERMS OF SERVICE. IF YOU DO NOT AGREE TO THE TOS, DO NOT USE THE BOT OR THE WEBSITE.
+
+The service is offered in the hopes that it will be useful, but without any warranty. The developer shall not be responsible or liable for the accuracy, correctness, usefulness or availability of any information transmitted or made available via bot or the website or any other officially associated media, and shall not be responsible or liable for any error or omissions in that information. The developer is also not liable for any damage, data loss or harm caused by the use or misuse of the bot or the website, however unlikely that may be. By using the bot or the website, you agree to this and you understand that the developer is not liable for damage, data loss or other harm, caused by the use or misuse of the bot or the website, or association with the developer in any other way.
+
+The developer reserves the right to modify, remove or revoke access to the service, whether to discontinue it as a whole or bar specific persons or parties from accessing or using the bot, the website, or any other officially associated media for any reason, including but not limited to:
+deliberately interfering with the service’s operation, such as by attempting a denial of service attack;
+using the service to break the law;
+using the service to break other organizations’ Terms of Service, including Discord.
+
+The developer will take whatever steps are necessary to preserve the integrity of the software and hardware the service runs upon, and again reserves the right to bar and take any reasonable measure against parties or persons who attempt to prevent that. In the event of a data breach, the developer will handle it in accordance with the law.
+
+Catalogger is proud to have good privacy practices and is GDPR compliant. You can find the privacy policy [here](/about/privacy).
+
+If you believe your intellectual property rights have been infringed by the developer, please contact us [here](/about/contact).
diff --git a/Catalogger.Frontend/src/routes/about/tos/+page.svelte b/Catalogger.Frontend/src/routes/about/tos/+page.svelte
new file mode 100644
index 0000000..bce6743
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/about/tos/+page.svelte
@@ -0,0 +1,9 @@
+
+
+
+ Catalogger - Terms of Service
+
+
+{@html html}
\ No newline at end of file
diff --git a/Catalogger.Frontend/src/routes/dash/[guildId]/+layout.svelte b/Catalogger.Frontend/src/routes/dash/[guildId]/+layout.svelte
index 56c8a36..353b812 100644
--- a/Catalogger.Frontend/src/routes/dash/[guildId]/+layout.svelte
+++ b/Catalogger.Frontend/src/routes/dash/[guildId]/+layout.svelte
@@ -69,6 +69,12 @@
>
Key roles
+
+ Delete all data
+
{#if $page.url.pathname === `/dash/${data.guild.id}` || $page.url.pathname === `/dash/${data.guild.id}/ignored-channels`}
diff --git a/Catalogger.Frontend/src/routes/dash/[guildId]/delete/+page.svelte b/Catalogger.Frontend/src/routes/dash/[guildId]/delete/+page.svelte
new file mode 100644
index 0000000..84b4862
--- /dev/null
+++ b/Catalogger.Frontend/src/routes/dash/[guildId]/delete/+page.svelte
@@ -0,0 +1,64 @@
+
+
+
Delete this server's data
+
+
+ To make Catalogger leave your server and delete all data from your server,
+ fill its name in below and press "Delete".
+
+
+ This is irreversible. If you change your mind later, your data cannot be
+ restored.
+
+
+ If you just want to make Catalogger leave your server but not delete data, simply
+ kick it via Discord.
+
+
+
+
This is irreversible!
+
+ We cannot help you recover data deleted in this way.
+
+
+
+
+
+
+
+
diff --git a/Catalogger.Frontend/vite.config.ts b/Catalogger.Frontend/vite.config.ts
index 5fc435e..c79886f 100644
--- a/Catalogger.Frontend/vite.config.ts
+++ b/Catalogger.Frontend/vite.config.ts
@@ -1,8 +1,9 @@
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
+import { Mode, plugin as markdown } from "vite-plugin-markdown";
export default defineConfig({
- plugins: [sveltekit()],
+ plugins: [sveltekit(), markdown({ mode: [Mode.HTML] })],
server: {
port: 5173,
strictPort: true,
diff --git a/Catalogger.Frontend/yarn.lock b/Catalogger.Frontend/yarn.lock
index 04ac7ac..492f46b 100644
--- a/Catalogger.Frontend/yarn.lock
+++ b/Catalogger.Frontend/yarn.lock
@@ -613,6 +613,13 @@ ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
argparse@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
@@ -762,6 +769,46 @@ devalue@^5.1.0:
resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.1.1.tgz#a71887ac0f354652851752654e4bd435a53891ae"
integrity sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==
+dom-serializer@^1.0.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+ integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.2.0"
+ entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.0.0, domhandler@^4.2.0:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+ integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+ dependencies:
+ domelementtype "^2.2.0"
+
+domutils@^2.5.2:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+ integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+ dependencies:
+ dom-serializer "^1.0.1"
+ domelementtype "^2.2.0"
+ domhandler "^4.2.0"
+
+entities@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+ integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
+entities@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
+ integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
+
esbuild@^0.21.3:
version "0.21.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d"
@@ -915,6 +962,11 @@ espree@^9.6.1:
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
esquery@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
@@ -1019,6 +1071,13 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a"
integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==
+front-matter@^4.0.0:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-4.0.2.tgz#b14e54dc745cfd7293484f3210d15ea4edd7f4d5"
+ integrity sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==
+ dependencies:
+ js-yaml "^3.13.1"
+
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
@@ -1068,6 +1127,16 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+htmlparser2@^6.0.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
+ integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.0.0"
+ domutils "^2.5.2"
+ entities "^2.0.0"
+
ignore@^5.2.0, ignore@^5.3.1:
version "5.3.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
@@ -1125,6 +1194,14 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+js-yaml@^3.13.1:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -1177,6 +1254,13 @@ lilconfig@^2.0.5:
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
+linkify-it@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
+ integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
+ dependencies:
+ uc.micro "^1.0.1"
+
locate-character@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974"
@@ -1201,11 +1285,27 @@ magic-string@^0.30.10, magic-string@^0.30.4, magic-string@^0.30.5:
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
+markdown-it@^12.0.0:
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
+ integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
+ dependencies:
+ argparse "^2.0.1"
+ entities "~2.1.0"
+ linkify-it "^3.0.1"
+ mdurl "^1.0.1"
+ uc.micro "^1.0.5"
+
mdn-data@2.0.30:
version "2.0.30"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
+mdurl@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+ integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==
+
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@@ -1485,6 +1585,11 @@ sirv@^3.0.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -1607,6 +1712,11 @@ typescript@^5.0.0:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
+uc.micro@^1.0.1, uc.micro@^1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
+ integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
+
uri-js@^4.2.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
@@ -1619,6 +1729,16 @@ util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+vite-plugin-markdown@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/vite-plugin-markdown/-/vite-plugin-markdown-2.2.0.tgz#a0c5bd7bb88f385fa4ea983bf51b1ee35057c90a"
+ integrity sha512-eH2tXMZcx3EHb5okd+/0VIyoR8Gp9pGe24UXitOOcGkzObbJ1vl48aGOAbakoT88FBdzC8MXNkMfBIB9VK0Ndg==
+ dependencies:
+ domhandler "^4.0.0"
+ front-matter "^4.0.0"
+ htmlparser2 "^6.0.0"
+ markdown-it "^12.0.0"
+
vite@^5.0.3:
version "5.4.9"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.9.tgz#215c80cbebfd09ccbb9ceb8c0621391c9abdc19c"
diff --git a/README.md b/README.md
index 81e44e2..9b9603c 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,24 @@ Add these with `dotnet nuget add source --username --password <
You must generate a personal access token (classic) [here](personal-access-token). Only give it the `read:packages` permission.
+## Deploying Catalogger yourself
+
+The bot itself should run on any server with .NET 8 and PostgreSQL 15 or later.
+A Redis-compatible database is *not* a hard dependency for the bot,
+but may be used for faster restarts (when Redis is used, certain data will be cached there instead of in memory).
+
+The dashboard isn't made for self-hosting. While it *should* work, the about/contact/ToS/privacy policy pages are hardcoded for our own convenience.
+
+For now, you'll also have to follow the instructions in the "Nuget" section above.
+
+Steps to build and run the bot:
+
+1. Clone the repository
+2. Run `dotnet publish` ([documentation][dotnet-publish])
+3. Change directory into `Catalogger.Backend/`
+4. Copy `config.example.ini` to `config.ini` and fill it out.
+5. Run the bot by executing `bin/Release/net8.0/Catalogger.Backend` (or net9.0 if you're using that version)
+
## License
Copyright (C) 2021-present sam (https://starshines.gay)
@@ -37,4 +55,5 @@ along with this program. If not, see .
[old-repo]: https://github.com/starshine-sys/catalogger/tree/main
[husky]: https://github.com/alirezanet/Husky.Net
[csharpier]: https://csharpier.com/
-[personal-access-token]: https://github.com/settings/tokens
\ No newline at end of file
+[personal-access-token]: https://github.com/settings/tokens
+[dotnet-publish]: https://learn.microsoft.com/en-us/dotnet/core/deploying/
\ No newline at end of file