Compare commits

...

27 Commits

Author SHA1 Message Date
defnotken
10cb6e9c2c update reference 2025-11-11 11:47:44 -06:00
defnotken
72b5b21624 Working User Info Changes. 2025-11-11 11:29:09 -06:00
defnotken
ac37020429 Lets try banners 2025-11-11 11:16:12 -06:00
defnotken
3b93ebb9e5 more logging 2025-11-11 11:04:39 -06:00
defnotken
44dc35ff99 logging image 2025-11-11 10:59:02 -06:00
defnotken
fac5b3caef Log warning 2025-11-11 10:54:24 -06:00
defnotken
f4ac99ba05 reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 2025-11-11 10:50:54 -06:00
defnotken
a63742df83 Lets try this again 2025-11-11 10:44:57 -06:00
defnotken
47fec97e81 Testing user info part 2 2025-11-11 10:29:54 -06:00
defnotken
32a9e93217 Testing profile in userinfo 2025-11-11 09:53:59 -06:00
defnotken
47b2a7a9b9 boolz 2025-10-29 18:26:41 -05:00
defnotken
7520bc1fa0 nullable 2025-10-29 18:09:06 -05:00
defnotken
700428eccb I'm blind 2025-10-29 17:56:09 -05:00
defnotken
9f97ab1eb4 Fixing some group controller stuff 2025-10-29 17:26:43 -05:00
defnotken
de0a37985a Update API 2025-10-29 17:23:32 -05:00
defnotken
1db5b7056d Initial Bot Cleanup + Profile Toggling 2025-10-29 17:23:07 -05:00
ee69df8081 Merge pull request 'Fixed some issues on group/user profiles' (#25) from syncshells-images-combined into master
Reviewed-on: #25
Reviewed-by: defnotken <defnotken@noreply.git.lightless-sync.org>
2025-10-27 20:19:27 +01:00
f29c874515 Merge branch 'master' into syncshells-images-combined 2025-10-27 20:19:03 +01:00
cake
b84d6c35d6 Moved notification on groups on new ones, Fixed new creation of profiles. 2025-10-27 19:08:25 +01:00
bd03fa6762 Syncshells Fix -
Reviewed-on: #24
written by: Abel / Cake
2025-10-26 19:48:18 +01:00
cake
8cde3b4933 Fixed image update from dto 2025-10-26 18:59:20 +01:00
defnotken
0c357aaf7c Merge remote-tracking branch 'origin/fix-images' into syncshells-images-combined 2025-10-26 12:31:34 -05:00
cake
ae09d79577 fix image results 2025-10-26 17:43:49 +01:00
azyges
cc24dc067e fix moderators + profiles 2025-10-27 00:59:56 +09:00
6ac56d38c0 Merge pull request 'lets try this' (#23) from sql-thing into master
Reviewed-on: #23
Reviewed-by: defnotken <defnotken@noreply.git.lightless-sync.org>
2025-10-21 23:26:14 +02:00
defnotken
b7f7381dec lets try this 2025-10-21 16:16:45 -05:00
1ce7a718bb Merge pull request 'Banner Support for profiles, Some cleanup/refactoring. Country for metrics.' (#22) from server-changes into master
Reviewed-on: #22
Reviewed-by: defnotken <defnotken@noreply.git.lightless-sync.org>
2025-10-21 22:50:54 +02:00
10 changed files with 401 additions and 109 deletions

View File

@@ -0,0 +1,56 @@
using LightlessSync.API.Dto.Group;
using LightlessSync.API.Routes;
using LightlessSyncShared.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LightlessSyncServer.Controllers;
[Route(LightlessAuth.Group)]
[Authorize(Policy = "Internal")]
public class GroupController : Controller
{
protected readonly ILogger Logger;
protected readonly IDbContextFactory<LightlessDbContext> LightlessDbContextFactory;
public GroupController(ILogger<GroupController> logger, IDbContextFactory<LightlessDbContext> lightlessDbContext)
{
Logger = logger;
LightlessDbContextFactory = lightlessDbContext;
}
[Route(LightlessAuth.Disable_Profile)]
[HttpPost]
public async Task DisableGroupProfile([FromBody] GroupProfileAvailabilityRequest request)
{
using var dbContext = await LightlessDbContextFactory.CreateDbContextAsync();
Logger.LogInformation("Disabling profile for group with GID {GID}", request.GID);
var group = await dbContext.GroupProfiles.FirstOrDefaultAsync(f => f.GroupGID == request.GID);
if (group != null)
{
group.ProfileDisabled = true;
}
await dbContext.SaveChangesAsync();
}
[Route(LightlessAuth.Enable_Profile)]
[HttpPost]
public async Task EnableGroupProfile([FromBody] GroupProfileAvailabilityRequest request)
{
using var dbContext = await LightlessDbContextFactory.CreateDbContextAsync();
Logger.LogInformation("Disabling profile for group with GID {GID}", request.GID);
var group = await dbContext.GroupProfiles.FirstOrDefaultAsync(f => f.GroupGID == request.GID);
if (group != null)
{
group.ProfileDisabled = false;
}
await dbContext.SaveChangesAsync();
}
}

View File

@@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace LightlessSyncAuthService.Controllers; namespace LightlessSyncServer.Controllers;
[Route(LightlessAuth.User)] [Route(LightlessAuth.User)]
[Authorize(Policy = "Internal")] [Authorize(Policy = "Internal")]
@@ -96,4 +96,38 @@ public class UserController : Controller
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
} }
[Route(LightlessAuth.Disable_Profile)]
[HttpPost]
public async Task DisableGroupProfile([FromBody] UserProfileAvailabilityRequest request)
{
using var dbContext = await LightlessDbContextFactory.CreateDbContextAsync();
Logger.LogInformation("Disabling profile for user with uid {UID}", request.UID);
var user = await dbContext.UserProfileData.FirstOrDefaultAsync(f => f.UserUID == request.UID);
if (user != null)
{
user.ProfileDisabled = true;
}
await dbContext.SaveChangesAsync();
}
[Route(LightlessAuth.Enable_Profile)]
[HttpPost]
public async Task EnableGroupProfile([FromBody] UserProfileAvailabilityRequest request)
{
using var dbContext = await LightlessDbContextFactory.CreateDbContextAsync();
Logger.LogInformation("Enabling profile for user with uid {UID}", request.UID);
var user = await dbContext.UserProfileData.FirstOrDefaultAsync(f => f.UserUID == request.UID);
if (user != null)
{
user.ProfileDisabled = false;
}
await dbContext.SaveChangesAsync();
}
} }

View File

@@ -211,7 +211,8 @@ public partial class LightlessHub
if (isOwnerResult.ReferredGroup == null) return (false, null); if (isOwnerResult.ReferredGroup == null) return (false, null);
var groupPairSelf = await DbContext.GroupPairs.SingleOrDefaultAsync(g => g.GroupGID == gid || g.Group.Alias == gid && g.GroupUserUID == UserUID).ConfigureAwait(false); var groupPairSelf = await DbContext.GroupPairs.SingleOrDefaultAsync(
g => (g.GroupGID == gid || g.Group.Alias == gid) && g.GroupUserUID == UserUID).ConfigureAwait(false);
if (groupPairSelf == null || !groupPairSelf.IsModerator) return (false, null); if (groupPairSelf == null || !groupPairSelf.IsModerator) return (false, null);
return (true, isOwnerResult.ReferredGroup); return (true, isOwnerResult.ReferredGroup);

View File

@@ -799,12 +799,12 @@ public partial class LightlessHub
if (!hasRights) return; if (!hasRights) return;
var groupProfileDb = await DbContext.GroupProfiles var groupProfileDb = await DbContext.GroupProfiles
.FirstOrDefaultAsync(g => g.Group.GID == dto.Group.GID || g.Group.Alias == dto.Group.GID, .Include(g => g.Group)
cancellationToken) .FirstOrDefaultAsync(g => g.GroupGID == dto.Group.GID, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
ImageCheckService.ImageLoadResult profileResult = null; ImageCheckService.ImageLoadResult profileResult = new();
ImageCheckService.ImageLoadResult bannerResult = null; ImageCheckService.ImageLoadResult bannerResult = new();
//Avatar image validation //Avatar image validation
if (!string.IsNullOrEmpty(dto.PictureBase64)) if (!string.IsNullOrEmpty(dto.PictureBase64))
@@ -830,27 +830,34 @@ public partial class LightlessHub
} }
} }
var sanitizedProfileImage = profileResult?.Base64Image;
var sanitizedBannerImage = bannerResult?.Base64Image;
if (groupProfileDb == null) if (groupProfileDb == null)
{ {
groupProfileDb = new GroupProfile groupProfileDb = new GroupProfile
{ {
GroupGID = dto.Group.GID, GroupGID = dto.Group.GID,
Group = group,
ProfileDisabled = false, ProfileDisabled = false,
IsNSFW = dto.IsNsfw ?? false, IsNSFW = dto.IsNsfw ?? false,
}; };
groupProfileDb.UpdateProfileFromDto(dto, profileResult.Base64Image, bannerResult.Base64Image); groupProfileDb.UpdateProfileFromDto(dto, sanitizedProfileImage, sanitizedBannerImage);
await DbContext.GroupProfiles.AddAsync(groupProfileDb, cancellationToken).ConfigureAwait(false); await DbContext.GroupProfiles.AddAsync(groupProfileDb, cancellationToken).ConfigureAwait(false);
} }
else else
{ {
groupProfileDb.Group ??= group;
if (groupProfileDb?.ProfileDisabled ?? false) if (groupProfileDb?.ProfileDisabled ?? false)
{ {
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile was permanently disabled and cannot be edited").ConfigureAwait(false); await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile was permanently disabled and cannot be edited").ConfigureAwait(false);
return; return;
} }
groupProfileDb.UpdateProfileFromDto(dto, profileResult.Base64Image, bannerResult.Base64Image); groupProfileDb.UpdateProfileFromDto(dto, sanitizedProfileImage, sanitizedBannerImage);
}
var userIds = await DbContext.GroupPairs var userIds = await DbContext.GroupPairs
.Where(p => p.GroupGID == groupProfileDb.GroupGID) .Where(p => p.GroupGID == groupProfileDb.GroupGID)
@@ -864,7 +871,6 @@ public partial class LightlessHub
await Clients.Users(userIds).Client_GroupSendProfile(profileDto) await Clients.Users(userIds).Client_GroupSendProfile(profileDto)
.ConfigureAwait(false); .ConfigureAwait(false);
} }
}
await DbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); await DbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
} }

View File

@@ -1128,7 +1128,7 @@ public partial class LightlessHub
if (!string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) throw new HubException("Cannot modify profile data for anyone but yourself"); if (!string.Equals(dto.User.UID, UserUID, StringComparison.Ordinal)) throw new HubException("Cannot modify profile data for anyone but yourself");
var existingData = await DbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == dto.User.UID, cancellationToken: RequestAbortedToken).ConfigureAwait(false); var profileData = await DbContext.UserProfileData.SingleOrDefaultAsync(u => u.UserUID == dto.User.UID, cancellationToken: RequestAbortedToken).ConfigureAwait(false);
ImageCheckService.ImageLoadResult profileResult = new(); ImageCheckService.ImageLoadResult profileResult = new();
ImageCheckService.ImageLoadResult bannerResult = new(); ImageCheckService.ImageLoadResult bannerResult = new();
@@ -1157,35 +1157,33 @@ public partial class LightlessHub
} }
} }
if (existingData != null) if (profileData != null)
{ {
if (existingData.FlaggedForReport) if (profileData.FlaggedForReport)
{ {
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile is currently flagged for report and cannot be edited").ConfigureAwait(false); await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile is currently flagged for report and cannot be edited").ConfigureAwait(false);
return; return;
} }
if (existingData.ProfileDisabled) if (profileData.ProfileDisabled)
{ {
await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile was permanently disabled and cannot be edited").ConfigureAwait(false); await Clients.Caller.Client_ReceiveServerMessage(MessageSeverity.Error, "Your profile was permanently disabled and cannot be edited").ConfigureAwait(false);
return; return;
} }
existingData.UpdateProfileFromDto(dto, profileResult.Base64Image, bannerResult.Base64Image); profileData.UpdateProfileFromDto(dto, profileResult.Base64Image, bannerResult.Base64Image);
} }
else else
{ {
UserProfileData newUserProfileData = new() profileData = new()
{ {
UserUID = dto.User.UID, UserUID = dto.User.UID,
Base64ProfileImage = dto.ProfilePictureBase64 ?? null,
UserDescription = dto.Description ?? null,
IsNSFW = dto.IsNSFW ?? false, IsNSFW = dto.IsNSFW ?? false,
}; };
existingData.UpdateProfileFromDto(dto, profileResult.Base64Image, bannerResult.Base64Image); profileData.UpdateProfileFromDto(dto, profileResult.Base64Image, bannerResult.Base64Image);
await DbContext.UserProfileData.AddAsync(newUserProfileData, cancellationToken).ConfigureAwait(false); await DbContext.UserProfileData.AddAsync(profileData, cancellationToken).ConfigureAwait(false);
} }

View File

@@ -1,6 +1,5 @@
using AspNetCoreRateLimit; using AspNetCoreRateLimit;
using LightlessSync.API.SignalR; using LightlessSync.API.SignalR;
using LightlessSyncAuthService.Controllers;
using LightlessSyncServer.Controllers; using LightlessSyncServer.Controllers;
using LightlessSyncServer.Configuration; using LightlessSyncServer.Configuration;
using LightlessSyncServer.Hubs; using LightlessSyncServer.Hubs;
@@ -73,7 +72,7 @@ public class Startup
a.FeatureProviders.Remove(a.FeatureProviders.OfType<ControllerFeatureProvider>().First()); a.FeatureProviders.Remove(a.FeatureProviders.OfType<ControllerFeatureProvider>().First());
if (lightlessConfig.GetValue<Uri>(nameof(ServerConfiguration.MainServerAddress), defaultValue: null) == null) if (lightlessConfig.GetValue<Uri>(nameof(ServerConfiguration.MainServerAddress), defaultValue: null) == null)
{ {
a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(LightlessServerConfigurationController), typeof(LightlessBaseConfigurationController), typeof(ClientMessageController), typeof(UserController))); a.FeatureProviders.Add(new AllowedControllersFeatureProvider(typeof(LightlessServerConfigurationController), typeof(LightlessBaseConfigurationController), typeof(ClientMessageController), typeof(UserController), typeof(GroupController)));
} }
else else
{ {

View File

@@ -17,22 +17,22 @@ public static class Extensions
if (profile == null || dto == null) return; if (profile == null || dto == null) return;
profile.Base64GroupProfileImage = string.IsNullOrWhiteSpace(base64PictureString) ? null : base64PictureString; if (base64PictureString != null) profile.Base64GroupProfileImage = base64PictureString;
profile.Base64GroupBannerImage = string.IsNullOrWhiteSpace(base64BannerString) ? null : base64BannerString; if (base64BannerString != null) profile.Base64GroupBannerImage = base64BannerString;
if (dto.Tags != null) profile.Tags = dto.Tags; if (dto.Tags != null) profile.Tags = dto.Tags;
if (dto.Description != null) profile.Description = dto.Description; if (dto.Description != null) profile.Description = dto.Description;
if (dto.IsNsfw.HasValue) profile.IsNSFW = dto.IsNsfw.Value; if (dto.IsNsfw.HasValue) profile.IsNSFW = dto.IsNsfw.Value;
} }
public static void UpdateProfileFromDto(this UserProfileData profile, UserProfileDto dto, string? base64PictureString, string? base64BannerString = null) public static void UpdateProfileFromDto(this UserProfileData profile, UserProfileDto dto, string? base64PictureString = null, string? base64BannerString = null)
{ {
ArgumentNullException.ThrowIfNull(profile); ArgumentNullException.ThrowIfNull(profile);
ArgumentNullException.ThrowIfNull(dto); ArgumentNullException.ThrowIfNull(dto);
if (profile == null || dto == null) return; if (profile == null || dto == null) return;
profile.Base64ProfileImage = string.IsNullOrWhiteSpace(base64PictureString) ? null : base64PictureString; if (base64PictureString != null) profile.Base64ProfileImage = base64PictureString;
profile.Base64BannerImage = string.IsNullOrWhiteSpace(base64BannerString) ? null : base64BannerString; if (base64BannerString != null) profile.Base64BannerImage = base64BannerString;
if (dto.Tags != null) profile.Tags = dto.Tags; if (dto.Tags != null) profile.Tags = dto.Tags;
if (dto.Description != null) profile.UserDescription = dto.Description; if (dto.Description != null) profile.UserDescription = dto.Description;
if (dto.IsNSFW.HasValue) profile.IsNSFW = dto.IsNSFW.Value; if (dto.IsNSFW.HasValue) profile.IsNSFW = dto.IsNSFW.Value;
@@ -45,7 +45,8 @@ public static class Extensions
return new GroupProfileDto(Group: null, Description: null, Tags: null, PictureBase64: null, BannerBase64: null, IsNsfw: false, IsDisabled: false); return new GroupProfileDto(Group: null, Description: null, Tags: null, PictureBase64: null, BannerBase64: null, IsNsfw: false, IsDisabled: false);
} }
var groupData = groupProfile.Group?.ToGroupData(); var groupData = groupProfile.Group?.ToGroupData()
?? (!string.IsNullOrWhiteSpace(groupProfile.GroupGID) ? new GroupData(groupProfile.GroupGID) : null);
return new GroupProfileDto( return new GroupProfileDto(
groupData, groupData,

View File

@@ -1,15 +1,17 @@
using Discord; using Discord;
using Discord.Interactions; using Discord.Interactions;
using LightlessSync.API.Data.Enum;
using LightlessSync.API.Dto.Group;
using LightlessSync.API.Dto.User;
using LightlessSyncShared.Data; using LightlessSyncShared.Data;
using LightlessSyncShared.Models;
using LightlessSyncShared.Services;
using LightlessSyncShared.Utils;
using LightlessSyncShared.Utils.Configuration;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Prometheus; using Prometheus;
using LightlessSyncShared.Models;
using LightlessSyncShared.Utils;
using LightlessSyncShared.Services;
using StackExchange.Redis; using StackExchange.Redis;
using LightlessSync.API.Data.Enum; using System.Net.Http.Json;
using LightlessSyncShared.Utils.Configuration;
using LightlessSync.API.Dto.User;
namespace LightlessSyncServices.Discord; namespace LightlessSyncServices.Discord;
@@ -43,7 +45,56 @@ public class LightlessModule : InteractionModuleBase
{ {
EmbedBuilder eb = new(); EmbedBuilder eb = new();
eb = await HandleUserInfo(eb, Context.User.Id, secondaryUid, discordUser?.Id ?? null, uid); using var scope = _services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<LightlessDbContext>();
await using (db.ConfigureAwait(false))
{
eb = await HandleUserInfo(eb, db, Context.User.Id, secondaryUid, discordUser?.Id ?? null, uid);
string uidToGet = await GetUserUID(db, secondaryUid, discordUser?.Id ?? null, uid).ConfigureAwait(false);
var profileData = await GetUserProfileData(db, uidToGet).ConfigureAwait(false);
if (profileData != null)
{
byte[] profileImage = GetProfileImage(profileData);
byte[] bannerImage = GetBannerImage(profileData);
using MemoryStream profileImgStream = new(profileImage);
using MemoryStream bannerImgStream = new(bannerImage);
eb.WithThumbnailUrl("attachment://profileimage.png");
eb.WithImageUrl("attachment://bannerimage.png");
await RespondWithFilesAsync(
new[] { new FileAttachment(profileImgStream, "profileimage.png"), new FileAttachment(bannerImgStream, "bannerimage.png") },
embeds: new[] { eb.Build() },
ephemeral: true).ConfigureAwait(false);
}
else
{
await RespondAsync(
embeds: new[] { eb.Build() },
ephemeral: true).ConfigureAwait(false);
}
}
}
catch (Exception ex)
{
EmbedBuilder eb = new();
eb.WithTitle("An error occured");
eb.WithDescription("Please report this error to bug-reports: " + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
await RespondAsync(embeds: new Embed[] { eb.Build() }, ephemeral: true).ConfigureAwait(false);
}
}
[SlashCommand("groupinfo", "Shows you your group profile information")]
public async Task GroupInfo([Summary("gid", "ADMIN ONLY: GID to check for")] string? uid = null)
{
_logger.LogInformation("SlashCommand:{userId}:{Method}",
Context.Interaction.User.Id, nameof(GroupInfo));
try
{
EmbedBuilder eb = new();
//eb = await HandleUserInfo(eb, Context.User.Id, secondaryUid, discordUser?.Id ?? null, uid);
await RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false); await RespondAsync(embeds: new[] { eb.Build() }, ephemeral: true).ConfigureAwait(false);
} }
@@ -110,10 +161,10 @@ public class LightlessModule : InteractionModuleBase
var testUri = new Uri(_lightlessServicesConfiguration.GetValue<Uri> var testUri = new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), "/msgc/sendMessage"); (nameof(ServicesConfiguration.MainServerAddress)), "/msgc/sendMessage");
await c.PostAsJsonAsync( using (await c.PostAsJsonAsync(
new Uri(_lightlessServicesConfiguration.GetValue<Uri>(nameof(ServicesConfiguration.MainServerAddress)), "/msgc/sendMessage"), new Uri(_lightlessServicesConfiguration.GetValue<Uri>(nameof(ServicesConfiguration.MainServerAddress)), "/msgc/sendMessage"),
new ClientMessage(messageType, message, uid ?? string.Empty) new ClientMessage(messageType, message, uid ?? string.Empty)
).ConfigureAwait(false); ).ConfigureAwait(false)) { }
var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null); var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
if (uid == null && discordChannelForMessages != null) if (uid == null && discordChannelForMessages != null)
@@ -146,20 +197,47 @@ public class LightlessModule : InteractionModuleBase
} }
} }
[SlashCommand("unbanbydiscord", "ADMIN ONLY: Unban a user by their discord ID")] [SlashCommand("serviceunban", "ADMIN ONLY: Unban a user by their discord ID or user ID [CHOOSE ONE ONLY]")]
public async Task UnbanByDiscord([Summary("discord_id", "Discord ID to unban")] string discordId) public async Task ServiceUnban(
[Summary("discord_id", "Discord ID to unban")] string? discordId = null,
[Summary("uid", "UID to unban")] string? uid = null
)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}", _logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Interaction.User.Id, nameof(UnbanByDiscord), Context.Interaction.User.Id, nameof(ServiceUnban),
string.Join(",", new[] { $"{nameof(discordId)}:{discordId}" })); string.Join(",", new[] { $"{nameof(discordId)}:{discordId}", $"{nameof(uid)}:{uid}" }));
try try
{ {
using HttpClient c = new HttpClient(); using HttpClient c = new HttpClient();
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token); c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
await c.PostAsJsonAsync(new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), "/user/unbanDiscord"), new UnbanRequest(string.Empty, discordId)) string endpoint;
.ConfigureAwait(false); UnbanRequest unbanRequest;
if (!string.IsNullOrEmpty(uid))
{
endpoint = "/user/unbanUID";
unbanRequest = new UnbanRequest(uid, string.Empty);
}
else if (!string.IsNullOrEmpty(discordId))
{
endpoint = "/user/unbanDiscord";
unbanRequest = new UnbanRequest(string.Empty, discordId);
}
else
{
await RespondAsync("You must provide either a UID or Discord ID.", ephemeral: true).ConfigureAwait(false);
return;
}
using (await c.PostAsJsonAsync(
new Uri(_lightlessServicesConfiguration.GetValue<Uri>(nameof(ServicesConfiguration.MainServerAddress)), endpoint),
unbanRequest).ConfigureAwait(false))
{
}
var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null); var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
if (discordChannelForMessages != null) if (discordChannelForMessages != null)
{ {
@@ -168,10 +246,12 @@ public class LightlessModule : InteractionModuleBase
{ {
var embedColor = Color.Blue; var embedColor = Color.Blue;
String idToUse = !string.IsNullOrEmpty(uid) ? uid : discordId;
EmbedBuilder eb = new(); EmbedBuilder eb = new();
eb.WithTitle("Unban Alert!"); eb.WithTitle("Unban Alert!");
eb.WithColor(embedColor); eb.WithColor(embedColor);
eb.WithDescription(discordId + " has been unbanned"); eb.WithDescription(idToUse + " has been unbanned");
await discordChannel.SendMessageAsync(embed: eb.Build()).ConfigureAwait(false); await discordChannel.SendMessageAsync(embed: eb.Build()).ConfigureAwait(false);
} }
@@ -188,64 +268,20 @@ public class LightlessModule : InteractionModuleBase
} }
} }
[SlashCommand("unbanbyuid", "ADMIN ONLY: Unban a user by their uid")] [SlashCommand("serviceban", "ADMIN ONLY: ban a user by their uid")]
public async Task UnbanByUID([Summary("uid", "uid to unban")] string uid) public async Task ServiceBan([Summary("uid", "uid to ban")] string uid)
{ {
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}", _logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Interaction.User.Id, nameof(UnbanByUID), Context.Interaction.User.Id, nameof(ServiceBan),
string.Join(",", new[] { $"{nameof(uid)}:{uid}" }));
try
{
using HttpClient c = new HttpClient();
var testUri = new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), "/user/unbanDiscord");
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
await c.PostAsJsonAsync(new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), "/user/unbanUID"), new UnbanRequest(uid, string.Empty))
.ConfigureAwait(false);
var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
if (discordChannelForMessages != null)
{
var discordChannel = await Context.Guild.GetChannelAsync(discordChannelForMessages.Value).ConfigureAwait(false) as IMessageChannel;
if (discordChannel != null)
{
var embedColor = Color.Blue;
EmbedBuilder eb = new();
eb.WithTitle("Unban Alert!");
eb.WithColor(embedColor);
eb.WithDescription(uid + " has been unbanned");
await discordChannel.SendMessageAsync(embed: eb.Build()).ConfigureAwait(false);
}
}
await RespondAsync("Message sent", ephemeral: true).ConfigureAwait(false);
}
catch (Exception ex)
{
EmbedBuilder eb = new();
eb.WithTitle("An error occured");
eb.WithDescription("Please report this: " + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
await RespondAsync(embeds: new Embed[] { eb.Build() }, ephemeral: true).ConfigureAwait(false);
}
}
[SlashCommand("markforban", "ADMIN ONLY: ban a user by their uid")]
public async Task MarkUidForBan([Summary("uid", "uid to ban")] string uid)
{
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Interaction.User.Id, nameof(MarkUidForBan),
string.Join(",", new[] { $"{nameof(uid)}:{uid}" })); string.Join(",", new[] { $"{nameof(uid)}:{uid}" }));
try try
{ {
using HttpClient c = new HttpClient(); using HttpClient c = new HttpClient();
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token); c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
await c.PostAsJsonAsync(new Uri(_lightlessServicesConfiguration.GetValue<Uri> using (await c.PostAsJsonAsync(new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), "/user/ban"), new BanRequest(uid)) (nameof(ServicesConfiguration.MainServerAddress)), "/user/ban"), new BanRequest(uid))
.ConfigureAwait(false); .ConfigureAwait(false)) { }
var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null); var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
if (discordChannelForMessages != null) if (discordChannelForMessages != null)
{ {
@@ -274,6 +310,100 @@ public class LightlessModule : InteractionModuleBase
} }
} }
[SlashCommand("toggleuserprofile", "ADMIN ONLY: disable a user profile by their uid")]
public async Task ToggleUserProfile(
[Summary("uid", "uid to disable")] string uid,
[Summary("toggle", "Enable or Disable the profile")]
[Choice("Enable", "Enable")]
[Choice("Disable", "Disable")] string toggle
)
{
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Interaction.User.Id, nameof(ToggleUserProfile),
string.Join(",", new[] { $"{nameof(uid)}:{uid}" }));
try
{
using HttpClient c = new HttpClient();
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
string endpoint = string.Equals(toggle, "Enable", StringComparison.Ordinal) ? "/user/enableProfile" : "/user/disableProfile";
using (await c.PostAsJsonAsync(new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), endpoint), new UserProfileAvailabilityRequest(uid))
.ConfigureAwait(false)) { }
var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
if (discordChannelForMessages != null)
{
var discordChannel = await Context.Guild.GetChannelAsync(discordChannelForMessages.Value).ConfigureAwait(false) as IMessageChannel;
if (discordChannel != null)
{
var embedColor = Color.Blue;
var action = string.Equals(toggle, "Enable", StringComparison.Ordinal) ? "enabled" : "disabled";
EmbedBuilder eb = new();
eb.WithTitle($"Profile {action}");
eb.WithColor(embedColor);
eb.WithDescription($"{uid}'s profile has been {action}");
await discordChannel.SendMessageAsync(embed: eb.Build()).ConfigureAwait(false);
}
}
await RespondAsync("Message sent", ephemeral: true).ConfigureAwait(false);
}
catch (Exception ex)
{
EmbedBuilder eb = new();
eb.WithTitle("An error occured");
eb.WithDescription("Please report this: " + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
await RespondAsync(embeds: new Embed[] { eb.Build() }, ephemeral: true).ConfigureAwait(false);
}
}
[SlashCommand("togglegroupprofile", "ADMIN ONLY: toggle a group profile by their gid")]
public async Task ToggleGroupProfile(
[Summary("gid", "gid to disable")] string gid,
[Summary("toggle", "Enable or Disable the profile")]
[Choice("Enable", "Enable")]
[Choice("Disable", "Disable")] string toggle
)
{
_logger.LogInformation("SlashCommand:{userId}:{Method}:{params}",
Context.Interaction.User.Id, nameof(ToggleUserProfile),
string.Join(",", new[] { $"{nameof(gid)}:{gid}" }));
try
{
using HttpClient c = new HttpClient();
c.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _serverTokenGenerator.Token);
string endpoint = string.Equals(toggle, "Enable", StringComparison.Ordinal) ? "/group/enableProfile" : "/group/disableProfile";
using (await c.PostAsJsonAsync(new Uri(_lightlessServicesConfiguration.GetValue<Uri>
(nameof(ServicesConfiguration.MainServerAddress)), endpoint), new GroupProfileAvailabilityRequest(gid))
.ConfigureAwait(false)) { }
var discordChannelForMessages = _lightlessServicesConfiguration.GetValueOrDefault<ulong?>(nameof(ServicesConfiguration.DiscordChannelForMessages), null);
if (discordChannelForMessages != null)
{
var discordChannel = await Context.Guild.GetChannelAsync(discordChannelForMessages.Value).ConfigureAwait(false) as IMessageChannel;
if (discordChannel != null)
{
var embedColor = Color.Blue;
var action = string.Equals(toggle, "Enable", StringComparison.Ordinal) ? "enabled" : "disabled";
EmbedBuilder eb = new();
eb.WithTitle($"Profile {action}");
eb.WithColor(embedColor);
eb.WithDescription($"{gid}'s profile has been {action}");
await discordChannel.SendMessageAsync(embed: eb.Build()).ConfigureAwait(false);
}
}
await RespondAsync("Message sent", ephemeral: true).ConfigureAwait(false);
}
catch (Exception ex)
{
EmbedBuilder eb = new();
eb.WithTitle("An error occured");
eb.WithDescription("Please report this: " + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace + Environment.NewLine);
await RespondAsync(embeds: new Embed[] { eb.Build() }, ephemeral: true).ConfigureAwait(false);
}
}
public async Task<Embed> HandleUserAdd(string desiredUid, ulong discordUserId) public async Task<Embed> HandleUserAdd(string desiredUid, ulong discordUserId)
{ {
var embed = new EmbedBuilder(); var embed = new EmbedBuilder();
@@ -319,11 +449,9 @@ public class LightlessModule : InteractionModuleBase
return embed.Build(); return embed.Build();
} }
private async Task<EmbedBuilder> HandleUserInfo(EmbedBuilder eb, ulong id, string? secondaryUserUid = null, ulong? optionalUser = null, string? uid = null) private async Task<EmbedBuilder> HandleUserInfo(EmbedBuilder eb, LightlessDbContext db, ulong id, string? secondaryUserUid = null, ulong? optionalUser = null, string? uid = null)
{ {
bool showForSecondaryUser = secondaryUserUid != null; bool showForSecondaryUser = secondaryUserUid != null;
using var scope = _services.CreateScope();
await using var db = scope.ServiceProvider.GetRequiredService<LightlessDbContext>();
var primaryUser = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == id).ConfigureAwait(false); var primaryUser = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == id).ConfigureAwait(false);
@@ -382,6 +510,7 @@ public class LightlessModule : InteractionModuleBase
var auth = await db.Auth.Include(u => u.PrimaryUser).SingleOrDefaultAsync(u => u.UserUID == dbUser.UID).ConfigureAwait(false); var auth = await db.Auth.Include(u => u.PrimaryUser).SingleOrDefaultAsync(u => u.UserUID == dbUser.UID).ConfigureAwait(false);
var groups = await db.Groups.Where(g => g.OwnerUID == dbUser.UID).ToListAsync().ConfigureAwait(false); var groups = await db.Groups.Where(g => g.OwnerUID == dbUser.UID).ToListAsync().ConfigureAwait(false);
var groupsJoined = await db.GroupPairs.Where(g => g.GroupUserUID == dbUser.UID).ToListAsync().ConfigureAwait(false); var groupsJoined = await db.GroupPairs.Where(g => g.GroupUserUID == dbUser.UID).ToListAsync().ConfigureAwait(false);
var profile = await db.UserProfileData.Where(u => u.UserUID == dbUser.UID).SingleOrDefaultAsync().ConfigureAwait(false);
var identity = await _connectionMultiplexer.GetDatabase().StringGetAsync("UID:" + dbUser.UID).ConfigureAwait(false); var identity = await _connectionMultiplexer.GetDatabase().StringGetAsync("UID:" + dbUser.UID).ConfigureAwait(false);
eb.WithTitle("User Information"); eb.WithTitle("User Information");
@@ -404,6 +533,14 @@ public class LightlessModule : InteractionModuleBase
eb.AddField("Secondary UIDs", string.Join(Environment.NewLine, secondaryUIDs)); eb.AddField("Secondary UIDs", string.Join(Environment.NewLine, secondaryUIDs));
} }
} }
if(profile != null)
{
eb.AddField("Profile Description", string.IsNullOrEmpty(profile.UserDescription) ? "(No description set)" : profile.UserDescription);
eb.AddField("Profile NSFW", profile.IsNSFW);
eb.AddField("Profile Disabled", profile.ProfileDisabled);
eb.AddField("Profile Flagged for Report", profile.FlaggedForReport);
eb.AddField("Profile Tags", profile.Tags != null && profile.Tags.Length > 0 ? string.Join(", ", profile.Tags) : "(No tags set)");
}
eb.AddField("Last Online (UTC)", dbUser.LastLoggedIn.ToString("U")); eb.AddField("Last Online (UTC)", dbUser.LastLoggedIn.ToString("U"));
eb.AddField("Currently online ", !string.IsNullOrEmpty(identity)); eb.AddField("Currently online ", !string.IsNullOrEmpty(identity));
eb.AddField("Hashed Secret Key", auth.HashedKey); eb.AddField("Hashed Secret Key", auth.HashedKey);
@@ -426,4 +563,62 @@ public class LightlessModule : InteractionModuleBase
return eb; return eb;
} }
private async Task<string> GetUserUID(LightlessDbContext db, string? secondaryUserUid = null, ulong? optionalUser = null, string? uid = null)
{
var primaryUser = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == Context.User.Id).ConfigureAwait(false);
ulong userToCheckForDiscordId = Context.User.Id;
if ((optionalUser != null || uid != null))
{
LodeStoneAuth userInDb = null;
if (optionalUser != null)
{
userInDb = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == optionalUser).ConfigureAwait(false);
}
else if (uid != null)
{
userInDb = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.User.UID == uid || u.User.Alias == uid).ConfigureAwait(false);
}
if (userInDb == null)
{
throw new Exception("The Discord user has no valid Lightless account");
}
userToCheckForDiscordId = userInDb.DiscordId;
}
var lodestoneUser = await db.LodeStoneAuth.Include(u => u.User).SingleOrDefaultAsync(u => u.DiscordId == userToCheckForDiscordId).ConfigureAwait(false);
var dbUser = lodestoneUser.User;
if (secondaryUserUid != null)
{
dbUser = (await db.Auth.Include(u => u.User).SingleOrDefaultAsync(u => u.PrimaryUserUID == dbUser.UID && u.UserUID == secondaryUserUid))?.User;
if (dbUser == null)
{
throw new Exception($"A secondary UID {secondaryUserUid} was not found attached to your primary UID {primaryUser.User.UID}.");
}
}
return dbUser.UID;
}
private byte[] GetProfileImage(UserProfileData profile)
{
if (profile != null && profile.Base64ProfileImage != null && profile.Base64ProfileImage.Length > 0)
{
return Convert.FromBase64String(profile.Base64ProfileImage);
}
return Array.Empty<byte>();
}
private byte[] GetBannerImage(UserProfileData profile)
{
if (profile != null && profile.Base64BannerImage != null && profile.Base64BannerImage.Length > 0)
{
return Convert.FromBase64String(profile.Base64BannerImage);
}
return Array.Empty<byte>();
}
private async Task<UserProfileData> GetUserProfileData(LightlessDbContext db, string uid)
{
var profile = await db.UserProfileData.Where(u => u.UserUID == uid).SingleOrDefaultAsync().ConfigureAwait(false);
return profile;
}
} }

View File

@@ -16,7 +16,9 @@ namespace LightlessSyncServer.Migrations
type: "integer[]", type: "integer[]",
nullable: true); nullable: true);
migrationBuilder.Sql("UPDATE group_profiles SET tags = NULL;"); migrationBuilder.Sql(
"ALTER TABLE group_profiles ALTER COLUMN tags TYPE integer[] USING string_to_array(tags, ',')::integer[];"
);
migrationBuilder.AlterColumn<int[]>( migrationBuilder.AlterColumn<int[]>(
name: "tags", name: "tags",