diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..449e4079
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+.vs/
+.vscode/
+_ReSharper.*/
\ No newline at end of file
diff --git a/CompatBot/CompatBot.csproj b/CompatBot/CompatBot.csproj
index 94c15ba0..3afdd19b 100644
--- a/CompatBot/CompatBot.csproj
+++ b/CompatBot/CompatBot.csproj
@@ -8,6 +8,7 @@
c2e6548b-b215-4a18-a010-958ef294b310
latest
1701;1702;VSTHRD200
+ Linux
@@ -37,6 +38,7 @@
+
diff --git a/CompatBot/Config.cs b/CompatBot/Config.cs
index 2b3b398d..6ace1493 100644
--- a/CompatBot/Config.cs
+++ b/CompatBot/Config.cs
@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
+using CompatBot.Utils;
using DSharpPlus.Entities;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.UserSecrets;
@@ -54,13 +55,16 @@ namespace CompatBot
public static int MinimumPiracyTriggerLength => config.GetValue(nameof(MinimumPiracyTriggerLength), 4);
public static string Token => config.GetValue(nameof(Token), "");
- public static string LogPath => config.GetValue(nameof(LogPath), "logs/bot.log"); // paths are relative to the assembly, so this will put it in the project's root
+ public static string LogPath => config.GetValue(nameof(LogPath), "../../../logs/"); // paths are relative to the assembly, so this will put it in the project's root
public static string IrdCachePath => config.GetValue(nameof(IrdCachePath), "./ird/");
public static string GoogleApiConfigPath
{
get
{
+ if (SandboxDetector.Detect() == "Docker")
+ return "/bot-config/credentials.json";
+
if (Assembly.GetEntryAssembly().GetCustomAttribute() is UserSecretsIdAttribute attribute)
{
var path = Path.GetDirectoryName(PathHelper.GetSecretsPathFromSecretsId(attribute.UserSecretsId));
@@ -68,6 +72,7 @@ namespace CompatBot
if (File.Exists(path))
return path;
}
+
return "Properties/credentials.json";
}
}
@@ -154,7 +159,7 @@ namespace CompatBot
config = new ConfigurationBuilder()
.AddUserSecrets(Assembly.GetExecutingAssembly()) // lower priority
- //.AddEnvironmentVariables()
+ .AddEnvironmentVariables()
.AddInMemoryCollection(inMemorySettings) // higher priority
.Build();
Log = GetLog();
@@ -172,7 +177,7 @@ namespace CompatBot
{
var config = new NLog.Config.LoggingConfiguration();
var fileTarget = new FileTarget("logfile") {
- FileName = Path.Combine("../../../", LogPath),
+ FileName = Path.Combine(LogPath, "bot.log"),
ArchiveEvery = FileArchivePeriod.Day,
ArchiveNumbering = ArchiveNumberingMode.DateAndSequence,
KeepFileOpen = true,
diff --git a/CompatBot/Database/DbImporter.cs b/CompatBot/Database/DbImporter.cs
index 4bb2b74b..3b772292 100644
--- a/CompatBot/Database/DbImporter.cs
+++ b/CompatBot/Database/DbImporter.cs
@@ -3,6 +3,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CompatBot.Database.Migrations;
+using CompatBot.Utils;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
@@ -110,6 +111,9 @@ namespace CompatBot.Database
internal static string GetDbPath(string dbName, Environment.SpecialFolder desiredFolder)
{
+ if (SandboxDetector.Detect() == "Docker")
+ return Path.Combine("/bot-db/", dbName);
+
var settingsFolder = Path.Combine(Environment.GetFolderPath(desiredFolder), "compat-bot");
try
{
diff --git a/CompatBot/Program.cs b/CompatBot/Program.cs
index b9b5cf23..c19d32a2 100644
--- a/CompatBot/Program.cs
+++ b/CompatBot/Program.cs
@@ -1,5 +1,6 @@
ο»Ώusing System;
using System.IO;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using CompatBot.Commands;
@@ -8,10 +9,12 @@ using CompatBot.Database;
using CompatBot.Database.Providers;
using CompatBot.EventHandlers;
using CompatBot.ThumbScrapper;
+using CompatBot.Utils;
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.Entities;
using DSharpPlus.Interactivity;
+using Microsoft.Extensions.Configuration.UserSecrets;
using Microsoft.Extensions.DependencyInjection;
namespace CompatBot
@@ -24,6 +27,17 @@ namespace CompatBot
internal static async Task Main(string[] args)
{
+ if (args.Length > 0 && args[0] == "--dry-run")
+ {
+ Console.WriteLine("Confinement: " + SandboxDetector.Detect());
+ Console.WriteLine("Database path: " + Path.GetDirectoryName(Path.GetFullPath(DbImporter.GetDbPath("fake.db", Environment.SpecialFolder.ApplicationData))));
+ if (Assembly.GetEntryAssembly().GetCustomAttribute() is UserSecretsIdAttribute attribute)
+ {
+ Console.WriteLine("Bot config path: " + Path.GetDirectoryName(Path.GetFullPath(Config.GoogleApiConfigPath)));
+ }
+ return;
+ }
+
var singleInstanceCheckThread = new Thread(() =>
{
using (var instanceLock = new Mutex(false, @"Global\RPCS3 Compatibility Bot"))
diff --git a/CompatBot/Properties/launchSettings.json b/CompatBot/Properties/launchSettings.json
index 978fca35..09e012f9 100644
--- a/CompatBot/Properties/launchSettings.json
+++ b/CompatBot/Properties/launchSettings.json
@@ -2,6 +2,9 @@
"profiles": {
"default": {
"commandName": "Project"
+ },
+ "Docker": {
+ "commandName": "Docker"
}
}
}
\ No newline at end of file
diff --git a/CompatBot/Utils/Extensions/StringUtils.cs b/CompatBot/Utils/Extensions/StringUtils.cs
index a9660c3c..bbaae199 100644
--- a/CompatBot/Utils/Extensions/StringUtils.cs
+++ b/CompatBot/Utils/Extensions/StringUtils.cs
@@ -218,7 +218,7 @@ namespace CompatBot.Utils
return result.ToString(0, result.Length-1);
}
- public static string GetMoons(decimal? stars)
+ public static string GetMoons(decimal? stars, bool haveFun = true)
{
if (!stars.HasValue)
return null;
@@ -232,7 +232,7 @@ namespace CompatBot.Utils
if (halfStar == 4)
{
- if (new Random().Next(100) == 69)
+ if (haveFun && new Random().Next(100) == 69)
result += "π";
else
result += "π";
@@ -246,7 +246,7 @@ namespace CompatBot.Utils
for (var i = 0; i < noStars; i++)
{
- if (i == 0 && halfStar == 0 && new Random().Next(100) == 69)
+ if (haveFun && i == 0 && halfStar == 0 && new Random().Next(100) == 69)
result += "π";
else
result += "π";
diff --git a/CompatBot/Utils/SandboxDetector.cs b/CompatBot/Utils/SandboxDetector.cs
index 2d3e33a7..4eb04efb 100644
--- a/CompatBot/Utils/SandboxDetector.cs
+++ b/CompatBot/Utils/SandboxDetector.cs
@@ -12,6 +12,9 @@ namespace CompatBot.Utils
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FLATPAK_SYSTEM_DIR")))
return "Flatpak";
+ if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("RUNNING_IN_DOCKER")))
+ return "Docker";
+
return null;
}
}
diff --git a/CompatBot/Utils/TimeParser.cs b/CompatBot/Utils/TimeParser.cs
index b3fb43f8..42e7c93d 100644
--- a/CompatBot/Utils/TimeParser.cs
+++ b/CompatBot/Utils/TimeParser.cs
@@ -82,5 +82,7 @@ namespace CompatBot.Utils
return date.ToUniversalTime();
return date.AsUtc();
}
+
+ public static List GetSupportedTimeZoneAbbreviations() => TimeZoneMap.Keys.ToList();
}
}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..b4c4e704
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+FROM mcr.microsoft.com/dotnet/core/sdk:latest AS base
+COPY packages /root/.nuget/packages/
+WORKDIR /src
+COPY . .
+RUN rm -rf ./packages
+RUN git status
+RUN dotnet build "CompatBot/CompatBot.csproj" -c Release
+ENV RUNNING_IN_DOCKER true
+WORKDIR /src/CompatBot
+RUN dotnet run -c Release --dry-run
+ENTRYPOINT ["dotnet", "run", "-c Release", "CompatBot.csproj"]
\ No newline at end of file
diff --git a/README.md b/README.md
index c0f0b3e1..c6cd2632 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,8 @@ How to Build
How to Run in Production
------------------------
+
+### Running from source
* Change configuration if needed (probably just token):
* use `$ dotnet user-secrets set Token `
* for available configuration variables, see [Config.cs](CompatBot/Config.cs#L31)
@@ -54,6 +56,11 @@ How to Run in Production
* `$ cd CompatBot`
* `$ dotnet run -c Release`
+### Running with Docker
+* Official image is hosted on [Docker Hub](https://hub.docker.com/r/13xforever/rpcs3-discord-bot).
+* You should pull images tagged with `release-latest` (same thing as `latest`)
+* Please take a look at the [docker-compose.yml](docker-compose.yml) for required configuration (bot token and mounting points for persistent data).
+
External resources that need manual updates
-------------------------------------------
* [Unicode confusables](http://www.unicode.org/Public/security/latest/confusables.txt) gzipped, for Homoglyph checks
diff --git a/Tests/IrdTests.cs b/Tests/IrdTests.cs
index 9220ef92..2b6e78a4 100644
--- a/Tests/IrdTests.cs
+++ b/Tests/IrdTests.cs
@@ -4,7 +4,7 @@ using NUnit.Framework;
namespace Tests
{
- [TestFixture]
+ [TestFixture, Explicit("Requires files to run")]
public class IrdTests
{
[Test]
diff --git a/Tests/StarsFormatTest.cs b/Tests/StarsFormatTest.cs
index 259afc05..294b572c 100644
--- a/Tests/StarsFormatTest.cs
+++ b/Tests/StarsFormatTest.cs
@@ -23,7 +23,7 @@ namespace Tests
[TestCase(1.0, "πππππ")]
public void FormatTest(decimal score, string expectedValue)
{
- Assert.That(StringUtils.GetMoons(score), Is.EqualTo(expectedValue), "Failed for " + score);
+ Assert.That(StringUtils.GetMoons(score, false), Is.EqualTo(expectedValue), "Failed for " + score);
}
}
}
diff --git a/Tests/TimeParserTests.cs b/Tests/TimeParserTests.cs
index 0a9cfe34..30406922 100644
--- a/Tests/TimeParserTests.cs
+++ b/Tests/TimeParserTests.cs
@@ -8,12 +8,12 @@ namespace Tests
public class TimeParserTests
{
[TestCase("2019-8-19 6:00 PT", "2019-08-19T13:00Z")]
- [TestCase("2019-8-19 17:00 bst", "2019-08-19T16:00Z")]
+ [TestCase("2019-8-19 17:00 cest", "2019-08-19T15:00Z")]
[TestCase("2019-9-1 22:00 jst", "2019-09-01T13:00Z")]
public void TimeZoneConverterTest(string input, string utcInput)
{
var utc = DateTime.Parse(utcInput).Normalize();
- Assert.That(TimeParser.TryParse(input, out var result), Is.True);
+ Assert.That(TimeParser.TryParse(input, out var result), Is.True, $"{input} failed to parse\nSupported time zones: {string.Join(", ", TimeParser.GetSupportedTimeZoneAbbreviations())}");
Assert.That(result, Is.EqualTo(utc));
Assert.That(result.Kind, Is.EqualTo(DateTimeKind.Utc));
}
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 985be7aa..e48f065a 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,20 +1,75 @@
-# ASP.NET Core
-# Build and test ASP.NET Core projects targeting .NET Core.
-# Add steps that run tests, create a NuGet package, deploy, and more:
-# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
+jobs:
+- job: BuildAndTest
+ displayName: 'Run tests'
+ pool:
+ vmImage: 'ubuntu-latest'
+ steps:
+ - script: dotnet restore --ignore-failed-sources
+ displayName: 'dotnet restore (first try)'
-pool:
- vmImage: 'Ubuntu 16.04'
+ - script: dotnet restore --ignore-failed-sources
+ displayName: 'dotnet restore (second try)'
-variables:
- buildConfiguration: 'Release'
+ - script: dotnet restore
+ displayName: 'dotnet restore (last try)'
-steps:
-- script: dotnet restore
- displayName: 'dotnet restore'
+ - script: dotnet build --configuration Debug
+ displayName: 'dotnet build Debug'
-- script: dotnet build --configuration Debug
- displayName: 'dotnet build Debug'
+ - task: DotNetCoreCLI@2
+ displayName: 'dotnet test'
+ inputs:
+ command: 'test'
+ projects: Tests/Tests.csproj
-- script: dotnet build --configuration Release
- displayName: 'dotnet build Release'
+- job: DockerImage
+ displayName: 'Build Docker image'
+ condition: and(and(succeeded(), eq(variables['ReleaseBranch'], variables['Build.SourceBranch'])), ne(format('{0}', variables['DockerConnection']), ''))
+ dependsOn: BuildAndTest
+ pool:
+ vmImage: 'ubuntu-latest'
+ steps:
+ - script: git checkout -f $(Build.SourceBranchName)
+ displayName: 'create local tracking branch'
+
+ - script: git clean -dfx
+ displayName: 'clean build artifacts'
+
+ - script: dotnet restore --ignore-failed-sources
+ displayName: 'dotnet restore (first try)'
+
+ - script: dotnet restore --ignore-failed-sources
+ displayName: 'dotnet restore (second try)'
+
+ - script: dotnet restore
+ displayName: 'dotnet restore (last try)'
+
+ - script: dotnet build --configuration Release
+ displayName: 'dotnet build Release'
+
+ - script: mkdir packages && cp -a /home/vsts/.nuget/packages ./packages/
+ displayName: 'copy nuget package cache for docker'
+
+ - task: Docker@2
+ displayName: 'building release docker image'
+ condition: and(succeeded(), eq(variables['ReleaseKind'], 'release'))
+ inputs:
+ containerRegistry: $(DockerConnection)
+ repository: $(DockerRegistry)
+ command: 'buildAndPush'
+ Dockerfile: 'Dockerfile'
+ tags: |
+ $(Build.BuildId)
+ release-latest
+ latest
+
+ - task: Docker@2
+ displayName: 'building test docker image'
+ condition: and(succeeded(), not(eq(variables['ReleaseKind'], 'release')))
+ inputs:
+ containerRegistry: $(DockerConnection)
+ repository: $(DockerRegistry)
+ command: 'buildAndPush'
+ Dockerfile: 'Dockerfile'
+ tags: test-latest
+
diff --git a/discord-bot-net.sln b/discord-bot-net.sln
index fb16dcae..45ae4504 100644
--- a/discord-bot-net.sln
+++ b/discord-bot-net.sln
@@ -1,7 +1,7 @@
ο»Ώ
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27703.2035
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28917.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompatBot", "CompatBot\CompatBot.csproj", "{6D9CA448-60C1-4D66-91D6-EC6C586508E6}"
EndProject
@@ -17,9 +17,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clients", "Clients", "{E7FE0ADD-CBA6-4321-8A1C-0A3B5C3F54C2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GithubClient", "Clients\GithubClient\GithubClient.csproj", "{AF8FDA29-864E-4A1C-9568-99DECB7E4B36}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GithubClient", "Clients\GithubClient\GithubClient.csproj", "{AF8FDA29-864E-4A1C-9568-99DECB7E4B36}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppveyorClient", "Clients\AppveyorClient\AppveyorClient.csproj", "{595ED201-1456-49F9-AD60-54B08499A5C1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AppveyorClient", "Clients\AppveyorClient\AppveyorClient.csproj", "{595ED201-1456-49F9-AD60-54B08499A5C1}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AD87F38F-BFCE-4EA6-A430-20C497552FD7}"
+ ProjectSection(SolutionItems) = preProject
+ azure-pipelines.yml = azure-pipelines.yml
+ docker-compose.example.yml = docker-compose.example.yml
+ Dockerfile = Dockerfile
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/docker-compose.example.yml b/docker-compose.example.yml
new file mode 100644
index 00000000..b92e1565
--- /dev/null
+++ b/docker-compose.example.yml
@@ -0,0 +1,13 @@
+version: "3"
+services:
+ bot:
+ image: rpcs3/discord-bot:latest
+ volumes:
+ - /home/MY_USER_NAME/.local/share/compat-bot:/bot-db
+ - /home/MY_USER_NAME/.microsoft/usersecrets/c2e6548b-b215-4a18-a010-958ef294b310:/bot-config
+ - /var/logs/compat-bot:/src/CompatBot/logs
+ - /ver/ird:/var/ird
+ environment:
+ Token: MY_BOT_TOKEN
+ LogPath: /var/logs/compat-bot
+ IrdCachePath: /var/ird