- small bot.cs cleanup

- creds.yml now loads and reloads properly (from the current directory, like credentials.json)
- added empty creds.yml to repo, and added it to .gitignore
This commit is contained in:
Kwoth
2021-06-24 15:43:53 +02:00
parent 16dd398aa0
commit a1ef862382
13 changed files with 215 additions and 208 deletions

10
.gitignore vendored
View File

@@ -1,18 +1,14 @@
#Manually added files
patreon_rewards.json
command_errors*.txt
_output/
src/NadekoBot/Command Errors*.txt
src/NadekoBot/credentials.json
# these 2 are used for migrations
NadekoBot.Core/credentials.json
NadekoBot.Core/credentials_example.json
src/NadekoBot/creds.yml
# credentials file after migrations
src/NadekoBot/old_credentials.json
src/NadekoBot/data/NadekoBot.db
src/NadekoBot/data/musicdata
# Created by https://www.gitignore.io/api/visualstudio,visualstudiocode,windows,linux,macos

View File

@@ -12,7 +12,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
@@ -43,26 +42,23 @@ namespace NadekoBot
public static Color ErrorColor { get; set; }
public static Color PendingColor { get; set; }
// todo remove ready prop
public TaskCompletionSource<bool> Ready { get; private set; } = new TaskCompletionSource<bool>();
public IServiceProvider Services { get; private set; }
public IDataCache Cache { get; private set; }
public string Mention { get; set; }
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
private readonly BotCredsProvider _credsProvider;
public Bot(int shardId)
{
if (shardId < 0)
throw new ArgumentOutOfRangeException(nameof(shardId));
TerribleElevatedPermissionCheck();
_creds = BotCredentialsProvider.CreateBotCredentials();
// todo no need for cache prop
Cache = new RedisCache(_creds, shardId);
_credsProvider = new BotCredsProvider();
_creds = _credsProvider.GetCreds();
_db = new DbService(_creds);
@@ -115,21 +111,21 @@ namespace NadekoBot
uow.EnsureUserCreated(_bot.Id, _bot.Username, _bot.Discriminator, _bot.AvatarId);
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
}
var svcs = new ServiceCollection()
.AddSingleton<IBotCredentials>(_creds)
.AddSingleton(_db)
.AddSingleton(Client)
.AddTransient<IBotCredentials>(_ => _creds) // bot creds
.AddSingleton(_db) // database
.AddRedis(_creds.RedisOptions) // redis
.AddSingleton(Client) // discord socket client
.AddSingleton(CommandService)
.AddSingleton(this)
.AddSingleton(Cache)
.AddSingleton(Cache.Redis)
.AddSingleton(this) // pepega
.AddSingleton<IDataCache, RedisCache>()
.AddSingleton<ISeria, JsonSeria>()
.AddSingleton<IPubSub, RedisPubSub>()
.AddSingleton<IConfigSeria, YamlSeria>()
.AddBotStringsServices()
.AddConfigServices()
.AddConfigMigrators()
.AddConfigMigrators() // todo remove config migrators
.AddMemoryCache()
.AddSingleton<IShopService, ShopService>()
// music
@@ -175,6 +171,7 @@ namespace NadekoBot
Log.Information($"All services loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
// todo remove config migrations
private void ApplyConfigMigrations()
{
// execute all migrators
@@ -192,6 +189,7 @@ namespace NadekoBot
// deleteBotConfig.ExecuteNonQuery();
}
// todo isn't there a built in for loading type readers?
private IEnumerable<object> LoadTypeReaders(Assembly assembly)
{
Type[] allTypes;
@@ -271,8 +269,10 @@ namespace NadekoBot
Client.Ready += SetClientReady;
await clientReady.Task.ConfigureAwait(false);
Client.Ready -= SetClientReady;
Client.JoinedGuild += Client_JoinedGuild;
Client.LeftGuild += Client_LeftGuild;
Log.Information("Shard {0} logged in.", Client.ShardId);
}
@@ -297,6 +297,7 @@ namespace NadekoBot
return Task.CompletedTask;
}
// todo cleanup
public async Task RunAsync()
{
var sw = Stopwatch.StartNew();
@@ -346,8 +347,10 @@ namespace NadekoBot
}
catch (Exception ex)
{
Log.Error(ex, "Failed running OnReadyAsync method on {Type} type: {Message}",
toExec.GetType().Name, ex.Message);
Log.Error(ex,
"Failed running OnReadyAsync method on {Type} type: {Message}",
toExec.GetType().Name,
ex.Message);
}
});
@@ -370,22 +373,8 @@ namespace NadekoBot
await Task.Delay(-1).ConfigureAwait(false);
}
private void TerribleElevatedPermissionCheck()
{
try
{
var rng = new NadekoRandom().Next(100000, 1000000);
var str = rng.ToString();
File.WriteAllText(str, str);
File.Delete(str);
}
catch
{
Log.Error("You must run the application as an ADMINISTRATOR");
Helpers.ReadErrorAndExit(2);
}
}
// todo status changes don't belong here
private void HandleStatusChanges()
{
var sub = Services.GetService<IDataCache>().Redis.GetSubscriber();

View File

@@ -9,8 +9,9 @@ namespace Nadeko.Common
{
public Creds()
{
Version = 1;
Token = string.Empty;
OwnerIds = new();
OwnerIds = new List<ulong>();
TotalShards = 1;
GoogleApiKey = string.Empty;
Votes = new(string.Empty, string.Empty);
@@ -23,15 +24,17 @@ namespace Nadeko.Common
Type = "sqlite",
ConnectionString = "Data Source=data/NadekoBot.db"
};
Version = 1;
}
[Comment(@"DO NOT CHANGE")]
public int Version { get; set; }
[Comment(@"Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/")]
public string Token { get; set; }
[Comment(@"List of Ids of the users who have bot owner permissions
**DO NOT ADD PEOPLE YOU DON'T TRUST**")]
public List<ulong> OwnerIds { get; set; }
public ICollection<ulong> OwnerIds { get; set; }
// todo update total shards on startup
[Comment(@"The number of shards that the bot will running on.
@@ -61,9 +64,6 @@ go to https://www.patreon.com/portal -> my clients -> create client")]
[Comment(@"Database options. Don't change if you don't know what you're doing. Leave null for default values")]
public DbOptions Db { get; set; }
[Comment(@"DO NOT CHANGE")]
public int Version { get; set; }
public RestartConfig RestartCommand { get; set; }
@@ -153,7 +153,7 @@ Used for cryptocurrency related commands.")]
public int TotalShards { get; set; } = 1;
public string PatreonAccessToken { get; set; } = string.Empty;
public string PatreonCampaignId { get; set; } = "334038";
public RestartConfig? RestartCommand { get; set; } = null;
public RestartConfig RestartCommand { get; set; } = null;
public string ShardRunCommand { get; set; } = string.Empty;
public string ShardRunArguments { get; set; } = string.Empty;

View File

@@ -8,6 +8,7 @@ using NadekoBot.Services;
using NadekoBot.Modules.Administration.Services;
using NadekoBot.Modules.Music.Resolvers;
using NadekoBot.Modules.Music.Services;
using StackExchange.Redis;
namespace NadekoBot.Extensions
{
@@ -65,5 +66,12 @@ namespace NadekoBot.Extensions
return services;
}
public static IServiceCollection AddRedis(this IServiceCollection services, string redisOptions)
{
var conf = ConfigurationOptions.Parse(redisOptions);
services.AddSingleton(ConnectionMultiplexer.Connect(conf));
return services;
}
}
}

View File

@@ -17,7 +17,7 @@ namespace NadekoBot.Services.Database
{
LogSetup.SetupLogger(-2);
var optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
IBotCredentials creds = BotCredentialsProvider.CreateBotCredentials();
IBotCredentials creds = new BotCredsProvider().GetCreds();
var builder = new SqliteConnectionStringBuilder(creds.Db.ConnectionString);
builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
optionsBuilder.UseSqlite(builder.ToString());

View File

@@ -38,11 +38,10 @@ namespace NadekoBot.Modules.Administration.Services
private readonly IImageCache _imgs;
private readonly IHttpClientFactory _httpFactory;
private readonly BotConfigService _bss;
private readonly ICoordinator _coord;
public SelfService(DiscordSocketClient client, CommandHandler cmdHandler, DbService db,
IBotStrings strings, IBotCredentials creds, IDataCache cache, IHttpClientFactory factory,
BotConfigService bss, ICoordinator coord)
BotConfigService bss)
{
_redis = cache.Redis;
_cmdHandler = cmdHandler;
@@ -54,7 +53,6 @@ namespace NadekoBot.Modules.Administration.Services
_imgs = cache.LocalImages;
_httpFactory = factory;
_bss = bss;
_coord = coord;
var sub = _redis.GetSubscriber();
if (_client.ShardId == 0)

View File

@@ -51,17 +51,12 @@
<PackageReference Include="YamlDotNet" Version="11.2.0" />
<PackageReference Include="YoutubeExplode" Version="6.0.2" />
<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.3.1" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="5.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<None Update="creds.yml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Protobuf Include="..\NadekoBot.Coordinator\Protos\coordinator.proto" GrpcServices="Client">
@@ -73,13 +68,7 @@
<None Update="nadeko_icon.ico;libopus.so;libsodium.so;libsodium.dll;opus.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="data\images_backup.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="data\xp_template_backup.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="creds_example.yml">
<None Update="creds_example.yml;data\images_backup.json;data\xp_template_backup.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

View File

@@ -10,7 +10,7 @@ namespace NadekoBot.Services
{
string Token { get; }
string GoogleApiKey { get; }
List<ulong> OwnerIds { get; }
ICollection<ulong> OwnerIds { get; }
string RapidApiKey { get; }
string PatreonAccessToken { get; }

View File

@@ -1,119 +1,63 @@
using Microsoft.Extensions.Configuration;
using System.IO;
using Microsoft.Extensions.Primitives;
using Nadeko.Common;
using NadekoBot.Common.Yml;
using Serilog;
namespace NadekoBot.Services
{
public static class BotCredentialsProvider
// todo check why is memory usage so unstable
public class BotCredsProvider
{
private const string _credsFileName = "creds.yml";
private static string _oldCredsJsonFilename = Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
private string CredsPath => Path.Combine(Directory.GetCurrentDirectory(), _credsFileName);
private const string _credsExampleFileName = "creds_example.yml";
private string CredsExamplePath => Path.Combine(Directory.GetCurrentDirectory(), _credsExampleFileName);
public static Creds CreateBotCredentials()
private string _oldCredsJsonFilename = Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
private Creds _creds = new Creds();
private IConfigurationRoot _config;
private readonly object reloadLock = new object();
private void Reload()
{
if (!File.Exists(_credsFileName))
Log.Warning($"{_credsFileName} is missing. " +
$"Attempting to load creds from environment variables prefixed with 'NadekoBot_'. " +
$"Example is in {Path.GetFullPath("./creds-example.yml")}");
IConfigurationBuilder configBuilder = new ConfigurationBuilder();
var creds = configBuilder
.AddYamlFile(_credsFileName, false, true)
.AddEnvironmentVariables("NadekoBot_")
.Build()
.Get<Creds>();
// if(string.IsNullOrWhiteSpace(creds.RedisOptions))
// creds.RedisOptions = ""
return creds;
// try
// {
//
//
// var data = configBuilder.Build();
//
// Token = data[nameof(Token)];
// if (string.IsNullOrWhiteSpace(Token))
// {
// Log.Error("Token is missing from credentials.json or Environment variables. Add it and restart the program.");
// Helpers.ReadErrorAndExit(5);
// }
//
// OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value))
// .ToImmutableArray();
// GoogleApiKey = data[nameof(GoogleApiKey)];
// MashapeKey = data[nameof(MashapeKey)];
// OsuApiKey = data[nameof(OsuApiKey)];
// PatreonAccessToken = data[nameof(PatreonAccessToken)];
// PatreonCampaignId = data[nameof(PatreonCampaignId)] ?? "334038";
// ShardRunCommand = data[nameof(ShardRunCommand)];
// ShardRunArguments = data[nameof(ShardRunArguments)];
// CleverbotApiKey = data[nameof(CleverbotApiKey)];
// LocationIqApiKey = data[nameof(LocationIqApiKey)];
// TimezoneDbApiKey = data[nameof(TimezoneDbApiKey)];
// CoinmarketcapApiKey = data[nameof(CoinmarketcapApiKey)];
// if (string.IsNullOrWhiteSpace(CoinmarketcapApiKey))
// {
// CoinmarketcapApiKey = "e79ec505-0913-439d-ae07-069e296a6079";
// }
//
// if (!string.IsNullOrWhiteSpace(data[nameof(RedisOptions)]))
// RedisOptions = data[nameof(RedisOptions)];
// else
// RedisOptions = "127.0.0.1,syncTimeout=3000";
//
// VotesToken = data[nameof(VotesToken)];
// VotesUrl = data[nameof(VotesUrl)];
// BotListToken = data[nameof(BotListToken)];
//
// var restartSection = data.GetSection(nameof(RestartCommand));
// var cmd = restartSection["cmd"];
// var args = restartSection["args"];
// if (!string.IsNullOrWhiteSpace(cmd))
// RestartCommand = new RestartConfig(cmd, args);
//
// if (Environment.OSVersion.Platform == PlatformID.Unix)
// {
// if (string.IsNullOrWhiteSpace(ShardRunCommand))
// ShardRunCommand = "dotnet";
// if (string.IsNullOrWhiteSpace(ShardRunArguments))
// ShardRunArguments = "run -c Release --no-build -- {0} {1}";
// }
// else //windows
// {
// if (string.IsNullOrWhiteSpace(ShardRunCommand))
// ShardRunCommand = "NadekoBot.exe";
// if (string.IsNullOrWhiteSpace(ShardRunArguments))
// ShardRunArguments = "{0} {1}";
// }
//
// if (!int.TryParse(data[nameof(TotalShards)], out var ts))
// ts = 0;
// TotalShards = ts < 1 ? 1 : ts;
//
// CarbonKey = data[nameof(CarbonKey)];
// var dbSection = data.GetSection("db");
// Db = new DBConfig(@"sqlite",
// string.IsNullOrWhiteSpace(dbSection["ConnectionString"])
// ? "Data Source=data/NadekoBot.db"
// : dbSection["ConnectionString"]);
//
// TwitchClientId = data[nameof(TwitchClientId)];
// if (string.IsNullOrWhiteSpace(TwitchClientId))
// {
// TwitchClientId = "67w6z9i09xv2uoojdm9l0wsyph4hxo6";
// }
// }
// catch (Exception ex)
// {
// Log.Error("JSON serialization has failed. Fix your credentials file and restart the bot.");
// Log.Fatal(ex.ToString());
// Helpers.ReadErrorAndExit(6);
// }
lock (reloadLock)
{
_creds.OwnerIds.Clear();
_config.Bind(_creds);
// todo load defaults for restart command, redis, and some others maybe?
}
}
public BotCredsProvider()
{
if (!File.Exists(CredsExamplePath))
{
File.WriteAllText(CredsExamplePath, Yaml.Serializer.Serialize(_creds));
}
if (!File.Exists(CredsPath))
{
Log.Warning($"{CredsPath} is missing. " +
$"Attempting to load creds from environment variables prefixed with 'NadekoBot_'. " +
$"Example is in {CredsExamplePath}");
}
_config = new ConfigurationBuilder()
.AddYamlFile(CredsPath, false, true)
.AddEnvironmentVariables("NadekoBot_")
.Build();
ChangeToken.OnChange(
() => _config.GetReloadToken(),
Reload);
Reload();
}
public Creds GetCreds() => _creds;
}
}

View File

@@ -5,6 +5,8 @@ using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
namespace NadekoBot.Services
{
@@ -18,14 +20,12 @@ namespace NadekoBot.Services
private readonly string _redisKey;
private readonly EndPoint _redisEndpoint;
public RedisCache(IBotCredentials creds, int shardId)
public RedisCache(ConnectionMultiplexer redis, IBotCredentials creds, DiscordSocketClient client)
{
var conf = ConfigurationOptions.Parse(creds.RedisOptions);
Redis = ConnectionMultiplexer.Connect(conf);
Redis = redis;
_redisEndpoint = Redis.GetEndPoints().First();
LocalImages = new RedisImagesCache(Redis, creds);
LocalData = new RedisLocalDataCache(Redis, creds, shardId);
LocalData = new RedisLocalDataCache(Redis, creds, client.ShardId);
_redisKey = creds.RedisKey();
}

View File

@@ -1,31 +0,0 @@
{
"Token": "",
"OwnerIds": [
105635576866156544
],
"GoogleApiKey": "",
"MashapeKey": "",
"OsuApiKey": "",
"SoundCloudClientId": "",
"CleverbotApiKey": "",
"CarbonKey": "",
"Db": {
"Type": "sqlite",
"ConnectionString": "Data Source=data/NadekoBot.db"
},
"TotalShards": 1,
"PatreonAccessToken": "",
"PatreonCampaignId": "334038",
"RestartCommand": null,
"ShardRunCommand": "",
"ShardRunArguments": "",
"ShardRunPort": null,
"BotListToken": null,
"TwitchClientId": null,
"VotesToken": null,
"VotesUrl": null,
"RedisOptions": null,
"LocationIqApiKey": null,
"TimezoneDbApiKey": null,
"CoinmarketcapApiKey": null
}

57
src/NadekoBot/creds.yml Normal file
View File

@@ -0,0 +1,57 @@
# DO NOT CHANGE
version: 1
# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
token:
# List of Ids of the users who have bot owner permissions
# **DO NOT ADD PEOPLE YOU DON'T TRUST**
ownerIds:
# The number of shards that the bot will running on.
# Leave at 1 if you don't know what you're doing.
totalShards: 1
# 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: ''
# Settings for voting system for discordbots. Meant for use on global Nadeko.
votes:
url: ''
key: ''
# Patreon auto reward system settings.
# go to https://www.patreon.com/portal -> my clients -> create client
patreon:
# Access token. You have to manually update this 1st of each month by refreshing the token on https://patreon.com/portal
accessToken: ''
# Unused atm
refreshToken: ''
# Unused atm
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: ''
# Api key for sending stats to DiscordBotList.
botListToken: ''
# Official cleverbot api key.
cleverbotApiKey: ''
# Redis connection string. Don't change if you don't know what you're doing.
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. Only sqlite supported atm
type: sqlite
# Connection string. Will default to "Data Source=data/NadekoBot.db"
connectionString: Data Source=data/NadekoBot.db
restartCommand:
votesUrl:
votesToken:
# Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)
rapidApiKey:
# https://locationiq.com api key (register and you will receive the token in the email).
# Used only for .time command.
locationIqApiKey:
# https://timezonedb.com api key (register and you will receive the token in the email).
# Used only for .time command
timezoneDbApiKey:
# https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
# Used for cryptocurrency related commands.
coinmarketcapApiKey:
# Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api
osuApiKey:

View File

@@ -0,0 +1,57 @@
# DO NOT CHANGE
version: 1
# Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/
token: ''
# List of Ids of the users who have bot owner permissions
# **DO NOT ADD PEOPLE YOU DON'T TRUST**
ownerIds:
# The number of shards that the bot will running on.
# Leave at 1 if you don't know what you're doing.
totalShards: 1
# 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: ''
# Settings for voting system for discordbots. Meant for use on global Nadeko.
votes:
url: ''
key: ''
# Patreon auto reward system settings.
# go to https://www.patreon.com/portal -> my clients -> create client
patreon:
# Access token. You have to manually update this 1st of each month by refreshing the token on https://patreon.com/portal
accessToken: ''
# Unused atm
refreshToken: ''
# Unused atm
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: ''
# Api key for sending stats to DiscordBotList.
botListToken: ''
# Official cleverbot api key.
cleverbotApiKey: ''
# Redis connection string. Don't change if you don't know what you're doing.
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. Only sqlite supported atm
type: sqlite
# Connection string. Will default to "Data Source=data/NadekoBot.db"
connectionString: Data Source=data/NadekoBot.db
restartCommand:
votesUrl:
votesToken:
# Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)
rapidApiKey:
# https://locationiq.com api key (register and you will receive the token in the email).
# Used only for .time command.
locationIqApiKey:
# https://timezonedb.com api key (register and you will receive the token in the email).
# Used only for .time command
timezoneDbApiKey:
# https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
# Used for cryptocurrency related commands.
coinmarketcapApiKey:
# Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api
osuApiKey: