Initial commit, working
This commit is contained in:
commit
795978c1e8
|
|
@ -0,0 +1,25 @@
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/.idea
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||||
|
USER $APP_UID
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["SquadBot.csproj", "./"]
|
||||||
|
RUN dotnet restore "SquadBot.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/"
|
||||||
|
RUN dotnet build "./SquadBot.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./SquadBot.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "SquadBot.dll"]
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
using NetCord.Gateway;
|
||||||
|
using NetCord.Hosting.Gateway;
|
||||||
|
using SquadBot.StateMangers;
|
||||||
|
|
||||||
|
namespace SquadBot.Handlers;
|
||||||
|
|
||||||
|
public class VoiceStatesHandler : IVoiceStateUpdateGatewayHandler
|
||||||
|
{
|
||||||
|
private ILogger _logger;
|
||||||
|
private VoiceStateManager _voiceStateManager;
|
||||||
|
public VoiceStatesHandler(ILogger<VoiceStatesHandler> Logger, VoiceStateManager vsManager)
|
||||||
|
{
|
||||||
|
_logger = Logger ?? throw new System.ArgumentNullException(nameof(Logger));
|
||||||
|
_voiceStateManager = vsManager ?? throw new System.ArgumentNullException(nameof(vsManager));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask HandleAsync(VoiceState voice_event)
|
||||||
|
{
|
||||||
|
if (voice_event.User == null) return;
|
||||||
|
await _voiceStateManager.HandleVoiceStateChange(voice_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using NetCord;
|
||||||
|
using NetCord.Gateway;
|
||||||
|
using NetCord.Hosting.Gateway;
|
||||||
|
using NetCord.Hosting.Rest;
|
||||||
|
using SquadBot.Handlers;
|
||||||
|
using SquadBot.StateMangers;
|
||||||
|
|
||||||
|
namespace SquadBot;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
|
||||||
|
public static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
var token = "";
|
||||||
|
var pubkey = "";
|
||||||
|
if (args.Length == 2)
|
||||||
|
{
|
||||||
|
token = args[0];
|
||||||
|
pubkey = args[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine("Usage: squadbot <token> <pubkey>");
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
builder.Services.AddLogging();
|
||||||
|
builder.Services.AddDiscordRest(options =>
|
||||||
|
{
|
||||||
|
options.PublicKey = pubkey;
|
||||||
|
options.Token = token;
|
||||||
|
});
|
||||||
|
builder.Services.AddSingleton<VoiceStateManager>();
|
||||||
|
builder.Services.AddSingleton<VoiceStatesHandler>();
|
||||||
|
builder.Services.AddDiscordGateway(options =>
|
||||||
|
{
|
||||||
|
options.Intents = GatewayIntents.GuildMessages
|
||||||
|
| GatewayIntents.GuildVoiceStates;
|
||||||
|
options.Token = token;
|
||||||
|
})
|
||||||
|
.AddGatewayHandlers(typeof(Program).Assembly);
|
||||||
|
|
||||||
|
var host = builder.Build()
|
||||||
|
.UseGatewayHandlers();
|
||||||
|
await host.RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "todos",
|
||||||
|
"applicationUrl": "http://localhost:13376",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace SquadBot.Providers;
|
||||||
|
|
||||||
|
public static class PhraseProvider
|
||||||
|
{
|
||||||
|
private static Random rng = new Random();
|
||||||
|
|
||||||
|
private static List<string> phrases = new List<string>()
|
||||||
|
{
|
||||||
|
"there is a new yapper in the voice chat.",
|
||||||
|
"someone is in the lobby, go irritate the shit out of them.",
|
||||||
|
"voice chat: Because typing is so last century.",
|
||||||
|
"come join the voice chat! If not we will talk about you.",
|
||||||
|
"join the voice chat. It's the only way to prove you're not a bot.",
|
||||||
|
"join the voice chat. We're not saying it'll be good, but it'll be something.",
|
||||||
|
"who needs friends in real life? Someone is in the voice chat!",
|
||||||
|
"someone is pretending to be productive in the voice chat.",
|
||||||
|
"the memes are better dubbed",
|
||||||
|
"voice chat's active. It's probably a disaster, but come anyway."
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetYapperPhrase()
|
||||||
|
{
|
||||||
|
return phrases[rng.Next(0, phrases.Count)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
<DebugSymbols>false</DebugSymbols>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="NetCord" Version="1.0.0-alpha.391" />
|
||||||
|
<PackageReference Include="NetCord.Hosting" Version="1.0.0-alpha.391" />
|
||||||
|
<PackageReference Include="NetCord.Hosting.AspNetCore" Version="1.0.0-alpha.391" />
|
||||||
|
<PackageReference Include="NetCord.Hosting.Services" Version="1.0.0-alpha.391" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
@SquadBot_HostAddress = http://localhost:13376
|
||||||
|
|
||||||
|
GET {{SquadBot_HostAddress}}/todos/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET {{SquadBot_HostAddress}}/todos/1
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SquadBot", "SquadBot.csproj", "{910F9D0B-51BD-4811-A2ED-DBD79BF02CCF}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{910F9D0B-51BD-4811-A2ED-DBD79BF02CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{910F9D0B-51BD-4811-A2ED-DBD79BF02CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{910F9D0B-51BD-4811-A2ED-DBD79BF02CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{910F9D0B-51BD-4811-A2ED-DBD79BF02CCF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{910F9D0B-51BD-4811-A2ED-DBD79BF02CCF}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
using NetCord;
|
||||||
|
using NetCord.Gateway;
|
||||||
|
using NetCord.Hosting.Gateway;
|
||||||
|
using NetCord.Rest;
|
||||||
|
using SquadBot.Providers;
|
||||||
|
|
||||||
|
namespace SquadBot.StateMangers;
|
||||||
|
|
||||||
|
public class VoiceStateManager
|
||||||
|
{
|
||||||
|
// Key: GuildId Value: List of UserId for users in voice channel
|
||||||
|
// TODO: We probably want to persist this somewhere else (Redis/Valkey)
|
||||||
|
private Dictionary<ulong, ISet<ulong>> _guildVoiceStates;
|
||||||
|
private Dictionary<ulong, DateTimeOffset> _yapperNotificationTimeouts;
|
||||||
|
private RestClient _restClient;
|
||||||
|
private ILogger<VoiceStateManager> _logger;
|
||||||
|
|
||||||
|
public VoiceStateManager(ILogger<VoiceStateManager> logger, RestClient restClient)
|
||||||
|
{
|
||||||
|
_guildVoiceStates = new Dictionary<ulong, ISet<ulong>>();
|
||||||
|
_yapperNotificationTimeouts = new Dictionary<ulong, DateTimeOffset>();
|
||||||
|
_restClient = restClient ?? throw new System.ArgumentNullException(nameof(restClient));
|
||||||
|
_logger = logger ?? throw new System.ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleVoiceStateChange(VoiceState voice_state)
|
||||||
|
{
|
||||||
|
if (voice_state.ChannelId is not null)
|
||||||
|
{
|
||||||
|
// Joining channel
|
||||||
|
_logger.LogInformation($"New yapper detected: {voice_state.User.Username}");
|
||||||
|
if (!_guildVoiceStates.TryGetValue(voice_state.GuildId, out var yappers) || yappers.Count < 0)
|
||||||
|
{
|
||||||
|
var channels = await _restClient.GetGuildChannelsAsync(voice_state.GuildId);
|
||||||
|
var alertChannelId = channels.FirstOrDefault(channel => channel.Name == "bot-tinkering")?.Id;
|
||||||
|
var guildRoles = await _restClient.GetGuildRolesAsync(voice_state.GuildId,
|
||||||
|
new RestRequestProperties() { AuditLogReason = "Role lookup" });
|
||||||
|
var yapperRoleId = guildRoles.FirstOrDefault(role => role.Name.ToLower() == "yapper")?.Id;
|
||||||
|
if (alertChannelId is not null && yapperRoleId is not null && (!_yapperNotificationTimeouts.TryGetValue(voice_state.GuildId, out var timeout) || DateTimeOffset.UtcNow.CompareTo(timeout) > 0))
|
||||||
|
{
|
||||||
|
// Notify that new yapper has arrived
|
||||||
|
await _restClient.SendMessageAsync(
|
||||||
|
alertChannelId.GetValueOrDefault(),
|
||||||
|
new MessageProperties()
|
||||||
|
{
|
||||||
|
AllowedMentions = new AllowedMentionsProperties() {AllowedRoles = [yapperRoleId.GetValueOrDefault()]},
|
||||||
|
Content = $"<@&{yapperRoleId.GetValueOrDefault()}> " + PhraseProvider.GetYapperPhrase()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
yappers = new HashSet<ulong>() { voice_state.UserId };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yappers = yappers.Append(voice_state.UserId).ToHashSet();
|
||||||
|
}
|
||||||
|
_guildVoiceStates[voice_state.GuildId] = yappers;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Leaving channel
|
||||||
|
_logger.LogInformation($"Yapper leaving: {voice_state.User.Username}");
|
||||||
|
if (_guildVoiceStates.TryGetValue(voice_state.GuildId, out var yappers))
|
||||||
|
{
|
||||||
|
yappers.Remove(voice_state.UserId);
|
||||||
|
_guildVoiceStates[voice_state.GuildId] = yappers;
|
||||||
|
_yapperNotificationTimeouts[voice_state.GuildId] = DateTimeOffset.UtcNow.AddHours(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"Discord": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"rollForward": "latestMajor",
|
||||||
|
"allowPrerelease": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue