From e01f48b2e94188ee1c8948d7b6d9cfbf426ad587 Mon Sep 17 00:00:00 2001 From: Toastie <toastie@toastiet0ast.com> Date: Thu, 13 Feb 2025 12:19:03 +1300 Subject: [PATCH] docker image builds and runs. Credential/error improvements --- .gitignore | 3 +- Dockerfile | 12 ++--- docker-entrypoint.sh | 22 ++++---- src/EllieBot/Bot.cs | 3 +- .../_common/Abstractions/Helpers/LogSetup.cs | 4 +- src/EllieBot/_common/Impl/BotCredsProvider.cs | 31 ++++++----- src/EllieBot/{ => data}/creds_example.yml | 51 ++++++++++++------- 7 files changed, 68 insertions(+), 58 deletions(-) rename src/EllieBot/{ => data}/creds_example.yml (89%) diff --git a/.gitignore b/.gitignore index 05687b8..fbb5781 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,7 @@ src/EllieBot/output src/EllieBot/creds.yml src/EllieBot/data/creds.yml src/EllieBot/Command Errors*.txt - -src/EllieBot/data/EllieBot.db +src/EllieBot/data/EllieBot.db.* # scripts ellie-menu.ps1 package.sh diff --git a/Dockerfile b/Dockerfile index c40314b..1a4e285 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,17 +32,17 @@ ADD --chmod=755 https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp # Create a new user, install dependencies, and set up sudoers file RUN apt update; \ - apt install -y --no-install-recommends ffmpeg python3; \ + apt install -y --no-install-recommends \ + libicu-dev ca-certificates \ + ffmpeg python3; \ apt autoremove -y; \ apt clean -y; +RUN update-ca-certificates + # Copy the built application and the entrypoint script from the build stage COPY --from=build /app ./ -COPY --chmod=755 docker-entrypoint.sh /usr/local/sbin/ - -# Set environment variables -ENV shard_id=0 -ENV total_shards=1 +COPY docker-entrypoint.sh /usr/local/sbin/ # Define the data directory as a volume VOLUME [ "/app/data" ] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 0ffa54a..385b868 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,16 +1,16 @@ #!/bin/sh -set -e; -data_init=/app/data_init -data=/app/data +set -e -# populate /app/data if empty -for i in $(ls $data_init) -do - if [ ! -e "$data/$i" ]; then - [ -f "$data_init/$i" ] && cp "$data_init/$i" "$data/$i" - [ -d "$data_init/$i" ] && cp -r "$data_init/$i" "$data/$i" - fi -done +data_init="/app/data_init" +data="/app/data" +# Merge data_init into data without overwrites. +cp -R -n "$data_init/." "$data/" + +echo "Yt-dlp update" +# TODO: Update yt-dlp. It should not crash the entrypoint if ca-certificates is not installed +# yt-dlp -U + +echo "Running EllieBot" exec "$@" \ No newline at end of file diff --git a/src/EllieBot/Bot.cs b/src/EllieBot/Bot.cs index 38b08ac..4c7a7cb 100644 --- a/src/EllieBot/Bot.cs +++ b/src/EllieBot/Bot.cs @@ -32,6 +32,7 @@ public sealed class Bot : IBot { ArgumentOutOfRangeException.ThrowIfLessThan(shardId, 0); + LogSetup.SetupLogger(shardId, null); ShardId = shardId; _credsProvider = new BotCredsProvider(totalShards); _creds = _credsProvider.GetCreds(); @@ -348,12 +349,10 @@ public sealed class Bot : IBot return Task.CompletedTask; } -#if GLOBAL_ELLIE || DEBUG if (arg.Exception is not null) Log.Warning(arg.Exception, "{ErrorSource} | {ErrorMessage}", arg.Source, arg.Message); else Log.Warning("{ErrorSource} | {ErrorMessage}", arg.Source, arg.Message); -#endif return Task.CompletedTask; } diff --git a/src/EllieBot/_common/Abstractions/Helpers/LogSetup.cs b/src/EllieBot/_common/Abstractions/Helpers/LogSetup.cs index 3d4edec..267c50b 100644 --- a/src/EllieBot/_common/Abstractions/Helpers/LogSetup.cs +++ b/src/EllieBot/_common/Abstractions/Helpers/LogSetup.cs @@ -6,7 +6,7 @@ namespace Ellie.Common; public static class LogSetup { - public static void SetupLogger(object source, IBotCreds creds) + public static void SetupLogger(object source, IBotCreds? creds) { var config = new LoggerConfiguration().MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Information) @@ -18,7 +18,7 @@ public static class LogSetup "[{Timestamp:HH:mm:ss} {Level:u3}] | #{LogSource} | {Message:lj}{NewLine}{Exception}") .Enrich.WithProperty("LogSource", source); - if (!string.IsNullOrWhiteSpace(creds.Seq.Url)) + if (!string.IsNullOrWhiteSpace(creds?.Seq.Url)) config = config.WriteTo.Seq(creds.Seq.Url, apiKey: creds.Seq.ApiKey); Log.Logger = config diff --git a/src/EllieBot/_common/Impl/BotCredsProvider.cs b/src/EllieBot/_common/Impl/BotCredsProvider.cs index ad94a38..3b03764 100644 --- a/src/EllieBot/_common/Impl/BotCredsProvider.cs +++ b/src/EllieBot/_common/Impl/BotCredsProvider.cs @@ -29,14 +29,18 @@ public sealed class BotCredsProvider : IBotCredsProvider CredsPath = Path.Combine(Directory.GetCurrentDirectory(), CREDS_FILE_NAME); CredsExamplePath = Path.Combine(Directory.GetCurrentDirectory(), CREDS_EXAMPLE_FILE_NAME); - try + if (!File.Exists(CredsExamplePath)) { - if (!File.Exists(CredsExamplePath)) + Log.Information("Creating data/creds_example.yml file..."); + try + { File.WriteAllText(CredsExamplePath, Yaml.Serializer.Serialize(_creds)); - } - catch - { - // this can fail in docker containers + } + catch (Exception ex) + { + // this can fail in docker containers + Log.Error(ex, "Error creating data/creds_example.yml file"); + } } @@ -53,12 +57,12 @@ public sealed class BotCredsProvider : IBotCredsProvider } _config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true) - .AddEnvironmentVariables("EllieBot_") + .AddEnvironmentVariables("bot_") .Build(); } catch (Exception ex) { - Console.WriteLine(ex.ToString()); + Log.Error(ex, "Error loading data/creds.yml file"); } Reload(); @@ -73,7 +77,7 @@ public sealed class BotCredsProvider : IBotCredsProvider if (string.IsNullOrWhiteSpace(_creds.Token)) { - Log.Error("Token is missing from creds.yml or Environment variables.\nAdd it and restart the program"); + Log.Error("Token is missing from data/creds.yml or Environment variables.\nAdd it and restart the program"); Helpers.ReadErrorAndExit(1); return; } @@ -127,14 +131,9 @@ public sealed class BotCredsProvider : IBotCredsProvider if (File.Exists(CREDS_FILE_NAME)) { var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME)); - if (creds.Version <= 5) + if (creds.Version < 20) { - creds.BotCache = BotCacheImplemenation.Memory; - } - - if (creds.Version < 13) - { - creds.Version = 13; + creds.Version = 20; File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds)); } } diff --git a/src/EllieBot/creds_example.yml b/src/EllieBot/data/creds_example.yml similarity index 89% rename from src/EllieBot/creds_example.yml rename to src/EllieBot/data/creds_example.yml index 977b597..b2ae00e 100644 --- a/src/EllieBot/creds_example.yml +++ b/src/EllieBot/data/creds_example.yml @@ -1,7 +1,7 @@ -# DO NOT CHANGE -version: 9 +# DO NOT CHANGE +version: 20 # Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/ -token: "" +token: '' # List of Ids of the users who have bot owner permissions # **DO NOT ADD PEOPLE YOU DON'T TRUST** ownerIds: [] @@ -9,7 +9,7 @@ ownerIds: [] usePrivilegedIntents: true # The number of shards that the bot will be running on. # Leave at 1 if you don't know what you're doing. -# +# # note: If you are planning to have more than one shard, then you must change botCache to 'redis'. # Also, in that case you should be using EllieBot.Coordinator to start the bot, and it will correctly override this value. totalShards: 1 @@ -23,15 +23,15 @@ ellieAiToken: # Login to https://console.cloud.google.com, create a new project, go to APIs & Services -> Library -> YouTube Data API and enable it. # Then, go to APIs and Services -> Credentials and click Create credentials -> API key. # Used only for Youtube Data Api (at the moment). -googleApiKey: "" +googleApiKey: '' # Create a new custom search here https://programmablesearchengine.google.com/cse/create/new # Enable SafeSearch # Remove all Sites to Search # Enable Search the entire web # Copy the 'Search Engine ID' to the SearchId field -# +# # Do all steps again but enable image search for the ImageSearchId -google: +google: searchId: imageSearchId: # Settings for voting system for discordbots. Meant for use on global Ellie. @@ -39,32 +39,32 @@ votes: # top.gg votes service url # This is the url of your instance of the EllieBot.Votes api # Example: https://votes.my.cool.bot.com - topggServiceUrl: "" + topggServiceUrl: '' # Authorization header value sent to the TopGG service url with each request # This should be equivalent to the TopggKey in your EllieBot.Votes api appsettings.json file - topggKey: "" + topggKey: '' # discords.com votes service url # This is the url of your instance of the EllieBot.Votes api # Example: https://votes.my.cool.bot.com - discordsServiceUrl: "" + discordsServiceUrl: '' # Authorization header value sent to the Discords service url with each request # This should be equivalent to the DiscordsKey in your EllieBot.Votes api appsettings.json file - discordsKey: "" + discordsKey: '' # Patreon auto reward system settings. # go to https://www.patreon.com/portal -> my clients -> create client patreon: clientId: - accessToken: "" - refreshToken: "" - clientSecret: "" + accessToken: '' + refreshToken: '' + clientSecret: '' # Campaign ID of your patreon page. Go to your patreon page (make sure you're logged in) and type "prompt('Campaign ID', window.patreon.bootstrap.creator.data.id);" in the console. (ctrl + shift + i) - campaignId: "" + campaignId: '' # Api key for sending stats to DiscordBotList. -botListToken: "" +botListToken: '' # Official cleverbot api key. -cleverbotApiKey: "" +cleverbotApiKey: '' # OpenAi api key. -gpt3ApiKey: "" +gpt3ApiKey: '' # Which cache implementation should bot use. # 'memory' - Cache will be in memory of the bot's process itself. Only use this on bots with a single shard. When the bot is restarted the cache is reset. # 'redis' - Uses redis (which needs to be separately downloaded and installed). The cache will persist through bot restarts. You can configure connection string in creds.yml @@ -74,7 +74,7 @@ botCache: Memory redisOptions: localhost:6379,syncTimeout=30000,responseTimeout=30000,allowAdmin=true,password= # Database options. Don't change if you don't know what you're doing. Leave null for default values db: - # Database type. "sqlite", "mysql" and "postgresql" are supported. + # Database type. "sqlite" and "postgresql" are supported. # Default is "sqlite" type: sqlite # Database connection string. @@ -119,3 +119,16 @@ twitchClientSecret: restartCommand: cmd: args: +# Settings for the grpc api. +# We don't provide support for this. +# If you leave certPath empty, the api will run on http. +grpcApi: + enabled: false + certChain: '' + certPrivateKey: '' + host: localhost + port: 43120 +# Url and api key to a seq server. If url is set, bot will try to send logs to it. +seq: + url: + apiKey: