mirror of
https://github.com/chylex/Discord-History-Tracker.git
synced 2025-06-15 06:42:09 +03:00
Store server icons
This commit is contained in:
parent
44b42657ef
commit
312be6609d
@ -142,11 +142,19 @@ const STATE = (function() {
|
|||||||
if (DISCORD.CHANNEL_TYPE.isPrivate(channelInfo.type)) {
|
if (DISCORD.CHANNEL_TYPE.isPrivate(channelInfo.type)) {
|
||||||
server.id = channelInfo.id;
|
server.id = channelInfo.id;
|
||||||
server.name = channel.name = getPrivateChannelName(channelInfo);
|
server.name = channel.name = getPrivateChannelName(channelInfo);
|
||||||
|
|
||||||
|
if (channelInfo.icon) {
|
||||||
|
server.icon = channelInfo.icon;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (serverInfo) {
|
else if (serverInfo) {
|
||||||
server.id = serverInfo.id;
|
server.id = serverInfo.id;
|
||||||
server.name = serverInfo.name;
|
server.name = serverInfo.name;
|
||||||
channel.name = channelInfo.name;
|
channel.name = channelInfo.name;
|
||||||
|
|
||||||
|
if (serverInfo.icon) {
|
||||||
|
server.icon = serverInfo.icon;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* @name DiscordGuild
|
* @name DiscordGuild
|
||||||
* @property {String} id
|
* @property {String} id
|
||||||
* @property {String} name
|
* @property {String} name
|
||||||
|
* @property {String|null|undefined} [icon]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,6 +15,7 @@
|
|||||||
* @property {Number} [position]
|
* @property {Number} [position]
|
||||||
* @property {String} [topic]
|
* @property {String} [topic]
|
||||||
* @property {Boolean} [nsfw]
|
* @property {Boolean} [nsfw]
|
||||||
|
* @property {String|null|undefined} [icon]
|
||||||
* @property {DiscordUser[]} [rawRecipients]
|
* @property {DiscordUser[]} [rawRecipients]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
using DHT.Server.Download;
|
||||||
|
|
||||||
namespace DHT.Server.Data;
|
namespace DHT.Server.Data;
|
||||||
|
|
||||||
public readonly struct Server {
|
public readonly struct Server {
|
||||||
public ulong Id { get; init; }
|
public ulong Id { get; init; }
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
public ServerType? Type { get; init; }
|
public ServerType? Type { get; init; }
|
||||||
|
public string? IconHash { get; init; }
|
||||||
|
|
||||||
|
internal FileUrl? IconUrl => Type == null || IconHash == null ? null : DownloadLinkExtractor.ServerIcon(Type.Value, Id, IconHash);
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,9 @@ static class ViewerJson {
|
|||||||
public sealed class JsonServer {
|
public sealed class JsonServer {
|
||||||
public required string Name { get; init; }
|
public required string Name { get; init; }
|
||||||
public required string Type { get; init; }
|
public required string Type { get; init; }
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public string? IconUrl { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class JsonChannel {
|
public sealed class JsonChannel {
|
||||||
|
@ -108,6 +108,7 @@ static class ViewerJsonExport {
|
|||||||
servers[server.Id] = new ViewerJson.JsonServer {
|
servers[server.Id] = new ViewerJson.JsonServer {
|
||||||
Name = server.Name,
|
Name = server.Name,
|
||||||
Type = ServerTypes.ToJsonViewerString(server.Type),
|
Type = ServerTypes.ToJsonViewerString(server.Type),
|
||||||
|
IconUrl = server.IconUrl?.DownloadUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,16 +394,6 @@ sealed class SqliteDownloadRepository(SqliteConnectionPool pool) : BaseSqliteRep
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await using (var cmd = conn.Command("SELECT id, avatar_url FROM users WHERE avatar_url IS NOT NULL")) {
|
|
||||||
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
|
||||||
|
|
||||||
while (await reader.ReadAsync(cancellationToken)) {
|
|
||||||
ulong id = reader.GetUint64(0);
|
|
||||||
string avatarHash = reader.GetString(1);
|
|
||||||
yield return DownloadLinkExtractor.UserAvatar(id, avatarHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await using (var cmd = conn.Command("SELECT DISTINCT emoji_id, emoji_flags FROM message_reactions WHERE emoji_id IS NOT NULL")) {
|
await using (var cmd = conn.Command("SELECT DISTINCT emoji_id, emoji_flags FROM message_reactions WHERE emoji_id IS NOT NULL")) {
|
||||||
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
||||||
|
|
||||||
@ -413,5 +403,29 @@ sealed class SqliteDownloadRepository(SqliteConnectionPool pool) : BaseSqliteRep
|
|||||||
yield return DownloadLinkExtractor.Emoji(emojiId, emojiFlags);
|
yield return DownloadLinkExtractor.Emoji(emojiId, emojiFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await using (var cmd = conn.Command("SELECT id, type, icon_hash FROM servers WHERE icon_hash IS NOT NULL")) {
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
||||||
|
|
||||||
|
while (await reader.ReadAsync(cancellationToken)) {
|
||||||
|
ulong id = reader.GetUint64(0);
|
||||||
|
ServerType? type = ServerTypes.FromString(reader.GetString(1));
|
||||||
|
string iconHash = reader.GetString(2);
|
||||||
|
|
||||||
|
if (DownloadLinkExtractor.ServerIcon(type, id, iconHash) is {} result) {
|
||||||
|
yield return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await using (var cmd = conn.Command("SELECT id, avatar_url FROM users WHERE avatar_url IS NOT NULL")) {
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
||||||
|
|
||||||
|
while (await reader.ReadAsync(cancellationToken)) {
|
||||||
|
ulong id = reader.GetUint64(0);
|
||||||
|
string avatarHash = reader.GetString(1);
|
||||||
|
yield return DownloadLinkExtractor.UserAvatar(id, avatarHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,9 @@ using Microsoft.Data.Sqlite;
|
|||||||
|
|
||||||
namespace DHT.Server.Database.Sqlite.Repositories;
|
namespace DHT.Server.Database.Sqlite.Repositories;
|
||||||
|
|
||||||
sealed class SqliteServerRepository : BaseSqliteRepository, IServerRepository {
|
sealed class SqliteServerRepository(SqliteConnectionPool pool, SqliteDownloadRepository downloads) : BaseSqliteRepository(Log), IServerRepository {
|
||||||
private static readonly Log Log = Log.ForType<SqliteServerRepository>();
|
private static readonly Log Log = Log.ForType<SqliteServerRepository>();
|
||||||
|
|
||||||
private readonly SqliteConnectionPool pool;
|
|
||||||
|
|
||||||
public SqliteServerRepository(SqliteConnectionPool pool) : base(Log) {
|
|
||||||
this.pool = pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Add(IReadOnlyList<Data.Server> servers) {
|
public async Task Add(IReadOnlyList<Data.Server> servers) {
|
||||||
await using (var conn = await pool.Take()) {
|
await using (var conn = await pool.Take()) {
|
||||||
await conn.BeginTransactionAsync();
|
await conn.BeginTransactionAsync();
|
||||||
@ -27,13 +21,18 @@ sealed class SqliteServerRepository : BaseSqliteRepository, IServerRepository {
|
|||||||
("id", SqliteType.Integer),
|
("id", SqliteType.Integer),
|
||||||
("name", SqliteType.Text),
|
("name", SqliteType.Text),
|
||||||
("type", SqliteType.Text),
|
("type", SqliteType.Text),
|
||||||
|
("icon_hash", SqliteType.Text),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
await using var downloadCollector = new SqliteDownloadRepository.NewDownloadCollector(downloads, conn);
|
||||||
|
|
||||||
foreach (Data.Server server in servers) {
|
foreach (Data.Server server in servers) {
|
||||||
cmd.Set(":id", server.Id);
|
cmd.Set(":id", server.Id);
|
||||||
cmd.Set(":name", server.Name);
|
cmd.Set(":name", server.Name);
|
||||||
cmd.Set(":type", ServerTypes.ToString(server.Type));
|
cmd.Set(":type", ServerTypes.ToString(server.Type));
|
||||||
|
cmd.Set(":icon_hash", server.IconHash);
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
await downloadCollector.AddIfNotNull(server.IconUrl?.ToPendingDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
await conn.CommitTransactionAsync();
|
await conn.CommitTransactionAsync();
|
||||||
@ -50,7 +49,7 @@ sealed class SqliteServerRepository : BaseSqliteRepository, IServerRepository {
|
|||||||
public async IAsyncEnumerable<Data.Server> Get([EnumeratorCancellation] CancellationToken cancellationToken) {
|
public async IAsyncEnumerable<Data.Server> Get([EnumeratorCancellation] CancellationToken cancellationToken) {
|
||||||
await using var conn = await pool.Take();
|
await using var conn = await pool.Take();
|
||||||
|
|
||||||
await using var cmd = conn.Command("SELECT id, name, type FROM servers");
|
await using var cmd = conn.Command("SELECT id, name, type, icon_hash FROM servers");
|
||||||
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
|
||||||
|
|
||||||
while (await reader.ReadAsync(cancellationToken)) {
|
while (await reader.ReadAsync(cancellationToken)) {
|
||||||
@ -58,6 +57,7 @@ sealed class SqliteServerRepository : BaseSqliteRepository, IServerRepository {
|
|||||||
Id = reader.GetUint64(0),
|
Id = reader.GetUint64(0),
|
||||||
Name = reader.GetString(1),
|
Name = reader.GetString(1),
|
||||||
Type = ServerTypes.FromString(reader.GetString(2)),
|
Type = ServerTypes.FromString(reader.GetString(2)),
|
||||||
|
IconHash = reader.IsDBNull(3) ? null : reader.GetString(3),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
app/Server/Database/Sqlite/Schema/SqliteSchemaUpgradeTo11.cs
Normal file
11
app/Server/Database/Sqlite/Schema/SqliteSchemaUpgradeTo11.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using DHT.Server.Database.Sqlite.Utils;
|
||||||
|
|
||||||
|
namespace DHT.Server.Database.Sqlite.Schema;
|
||||||
|
|
||||||
|
sealed class SqliteSchemaUpgradeTo11 : ISchemaUpgrade {
|
||||||
|
async Task ISchemaUpgrade.Run(ISqliteConnection conn, ISchemaUpgradeCallbacks.IProgressReporter reporter) {
|
||||||
|
await reporter.MainWork("Applying schema changes...", finishedItems: 0, totalItems: 1);
|
||||||
|
await conn.ExecuteAsync("ALTER TABLE servers ADD icon_hash TEXT");
|
||||||
|
}
|
||||||
|
}
|
@ -66,16 +66,16 @@ public sealed class SqliteDatabaseFile : IDatabaseFile {
|
|||||||
downloads = new SqliteDownloadRepository(pool);
|
downloads = new SqliteDownloadRepository(pool);
|
||||||
settings = new SqliteSettingsRepository(pool);
|
settings = new SqliteSettingsRepository(pool);
|
||||||
users = new SqliteUserRepository(pool, downloads);
|
users = new SqliteUserRepository(pool, downloads);
|
||||||
servers = new SqliteServerRepository(pool);
|
servers = new SqliteServerRepository(pool, downloads);
|
||||||
channels = new SqliteChannelRepository(pool);
|
channels = new SqliteChannelRepository(pool);
|
||||||
messages = new SqliteMessageRepository(pool, downloads);
|
messages = new SqliteMessageRepository(pool, downloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public async ValueTask DisposeAsync() {
|
||||||
users.Dispose();
|
|
||||||
servers.Dispose();
|
|
||||||
channels.Dispose();
|
|
||||||
messages.Dispose();
|
messages.Dispose();
|
||||||
|
channels.Dispose();
|
||||||
|
servers.Dispose();
|
||||||
|
users.Dispose();
|
||||||
downloads.Dispose();
|
downloads.Dispose();
|
||||||
await pool.DisposeAsync();
|
await pool.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ using Microsoft.Data.Sqlite;
|
|||||||
namespace DHT.Server.Database.Sqlite;
|
namespace DHT.Server.Database.Sqlite;
|
||||||
|
|
||||||
sealed class SqliteSchema(CustomSqliteConnection conn) {
|
sealed class SqliteSchema(CustomSqliteConnection conn) {
|
||||||
internal const int Version = 10;
|
internal const int Version = 11;
|
||||||
|
|
||||||
private static readonly Log Log = Log.ForType<SqliteSchema>();
|
private static readonly Log Log = Log.ForType<SqliteSchema>();
|
||||||
|
|
||||||
@ -94,7 +94,8 @@ sealed class SqliteSchema(CustomSqliteConnection conn) {
|
|||||||
CREATE TABLE servers (
|
CREATE TABLE servers (
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT NOT NULL
|
type TEXT NOT NULL,
|
||||||
|
icon_hash TEXT
|
||||||
)
|
)
|
||||||
""");
|
""");
|
||||||
|
|
||||||
@ -222,6 +223,7 @@ sealed class SqliteSchema(CustomSqliteConnection conn) {
|
|||||||
{ 7, new SqliteSchemaUpgradeTo8() },
|
{ 7, new SqliteSchemaUpgradeTo8() },
|
||||||
{ 8, new SqliteSchemaUpgradeTo9() },
|
{ 8, new SqliteSchemaUpgradeTo9() },
|
||||||
{ 9, new SqliteSchemaUpgradeTo10() },
|
{ 9, new SqliteSchemaUpgradeTo10() },
|
||||||
|
{ 10, new SqliteSchemaUpgradeTo11() },
|
||||||
};
|
};
|
||||||
|
|
||||||
Perf perf = Log.Start("from version " + dbVersion);
|
Perf perf = Log.Start("from version " + dbVersion);
|
||||||
|
@ -12,6 +12,14 @@ namespace DHT.Server.Download;
|
|||||||
static class DownloadLinkExtractor {
|
static class DownloadLinkExtractor {
|
||||||
private static readonly Log Log = Log.ForType(typeof(DownloadLinkExtractor));
|
private static readonly Log Log = Log.ForType(typeof(DownloadLinkExtractor));
|
||||||
|
|
||||||
|
public static FileUrl? ServerIcon(ServerType? type, ulong id, string iconHash) {
|
||||||
|
return type switch {
|
||||||
|
ServerType.Server => new FileUrl($"https://cdn.discordapp.com/icons/{id}/{iconHash}.webp", MediaTypeNames.Image.Webp),
|
||||||
|
ServerType.Group => new FileUrl($"https://cdn.discordapp.com/channel-icons/{id}/{iconHash}.webp", MediaTypeNames.Image.Webp),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static FileUrl UserAvatar(ulong id, string avatarHash) {
|
public static FileUrl UserAvatar(ulong id, string avatarHash) {
|
||||||
return new FileUrl($"https://cdn.discordapp.com/avatars/{id}/{avatarHash}.webp", MediaTypeNames.Image.Webp);
|
return new FileUrl($"https://cdn.discordapp.com/avatars/{id}/{avatarHash}.webp", MediaTypeNames.Image.Webp);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ sealed class TrackChannelEndpoint(IDatabaseFile db) : BaseEndpoint {
|
|||||||
Id = json.RequireSnowflake("id", path),
|
Id = json.RequireSnowflake("id", path),
|
||||||
Name = json.RequireString("name", path),
|
Name = json.RequireString("name", path),
|
||||||
Type = ServerTypes.FromString(json.RequireString("type", path)) ?? throw new HttpException(HttpStatusCode.BadRequest, "Server type must be either 'SERVER', 'GROUP', or 'DM'."),
|
Type = ServerTypes.FromString(json.RequireString("type", path)) ?? throw new HttpException(HttpStatusCode.BadRequest, "Server type must be either 'SERVER', 'GROUP', or 'DM'."),
|
||||||
|
IconHash = json.HasKey("icon") ? json.RequireString("icon", path) : null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
app/empty.dht
BIN
app/empty.dht
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user