This commit is contained in:
Kwoth
2021-11-18 16:41:40 +01:00
13 changed files with 373 additions and 265 deletions

View File

@@ -4,6 +4,16 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
## Unreleased
### Added
- Patreon Access and Refresh Tokens should now be automatically updated once a month as long as the user has provided the necessary credentials in creds.yml file:
- `Patreon.ClientId`
- `Patreon.RefreshToken` (will also get updated once a month but needs an initial value)
- `Patreon.ClientSecret`
- `Patreon.CampaignId`
### Fixed
- Fixed an error that would show up in the console when a club image couldn't be drawn in certain circumstances
## [3.0.8] - 03.11.2021
### Added

View File

@@ -28,7 +28,7 @@ namespace NadekoBot
private readonly IBotCredentials _creds;
private readonly CommandService _commandService;
private readonly DbService _db;
private readonly BotCredsProvider _credsProvider;
private readonly IBotCredsProvider _credsProvider;
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
@@ -95,8 +95,8 @@ namespace NadekoBot
}
var svcs = new ServiceCollection()
.AddTransient<IBotCredentials>(_ => _creds) // bot creds
.AddSingleton(_credsProvider)
.AddTransient<IBotCredentials>(_ => _credsProvider.GetCreds()) // bot creds
.AddSingleton<IBotCredsProvider>(_credsProvider)
.AddSingleton(_db) // database
.AddRedis(_creds.RedisOptions) // redis
.AddSingleton(Client) // discord socket client

View File

@@ -12,7 +12,7 @@ namespace NadekoBot.Common.Attributes
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo executingCommand, IServiceProvider services)
{
var creds = services.GetRequiredService<BotCredsProvider>().GetCreds();
var creds = services.GetRequiredService<IBotCredsProvider>().GetCreds();
return Task.FromResult((creds.IsOwner(context.User) || context.Client.CurrentUser.Id == context.User.Id ? PreconditionResult.FromSuccess() : PreconditionResult.FromError("Not owner")));
}

View File

@@ -73,11 +73,6 @@ go to https://www.patreon.com/portal -> my clients -> create client")]
Change only if you've changed the coordinator address or port.")]
public string CoordinatorUrl { get; set; }
[YamlIgnore]
public string PatreonCampaignId => Patreon?.CampaignId;
[YamlIgnore]
public string PatreonAccessToken => Patreon?.AccessToken;
[Comment(@"Api key obtained on https://rapidapi.com (go to MyApps -> Add New App -> Enter Name -> Application key)")]
public string RapidApiKey { get; set; }
@@ -121,11 +116,9 @@ Windows default
// todo fixup patreon
public sealed record PatreonSettings
{
[Comment(@"Access token. You have to manually update this 1st of each month by refreshing the token on https://patreon.com/portal")]
public string ClientId { get; set; }
public string AccessToken { get; set; }
[Comment(@"Unused atm")]
public string RefreshToken { get; set; }
[Comment(@"Unused atm")]
public string ClientSecret { get; set; }
[Comment(@"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)")]

View File

@@ -12,12 +12,11 @@ namespace NadekoBot
string GoogleApiKey { get; }
ICollection<ulong> OwnerIds { get; }
string RapidApiKey { get; }
string PatreonAccessToken { get; }
Creds.DbOptions Db { get; }
string OsuApiKey { get; }
int TotalShards { get; }
string PatreonCampaignId { get; }
Creds.PatreonSettings Patreon { get; }
string CleverbotApiKey { get; }
RestartConfig RestartCommand { get; }
Creds.VotesSettings Votes { get; }

View File

@@ -2,6 +2,7 @@
using Discord.Commands;
using System;
using System.Threading.Tasks;
using NadekoBot.Common;
using NadekoBot.Common.Attributes;
using NadekoBot.Services;
using NadekoBot.Db;
@@ -23,6 +24,7 @@ namespace NadekoBot.Modules.Games
_db = db;
}
[NoPublicBot]
[NadekoCommand, Aliases]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)]

View File

@@ -1,23 +1,134 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Utility.Common.Patreon
{
public class PatreonData
public sealed class Attributes
{
public JObject[] Included { get; set; }
public JObject[] Data { get; set; }
public PatreonDataLinks Links { get; set; }
[JsonPropertyName("full_name")]
public string FullName { get; set; }
[JsonPropertyName("is_follower")]
public bool IsFollower { get; set; }
[JsonPropertyName("last_charge_date")]
public DateTime LastChargeDate { get; set; }
[JsonPropertyName("last_charge_status")]
public string LastChargeStatus { get; set; }
[JsonPropertyName("lifetime_support_cents")]
public int LifetimeSupportCents { get; set; }
[JsonPropertyName("currently_entitled_amount_cents")]
public int CurrentlyEntitledAmountCents { get; set; }
[JsonPropertyName("patron_status")]
public string PatronStatus { get; set; }
}
public class PatreonDataLinks
public sealed class Data
{
public string first { get; set; }
public string next { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
}
public class PatreonUserAndReward
public sealed class Address
{
public PatreonUser User { get; set; }
public PatreonPledge Reward { get; set; }
[JsonPropertyName("data")]
public Data Data { get; set; }
}
// public sealed class CurrentlyEntitledTiers
// {
// [JsonPropertyName("data")]
// public List<Datum> Data { get; set; }
// }
// public sealed class Relationships
// {
// [JsonPropertyName("address")]
// public Address Address { get; set; }
//
// // [JsonPropertyName("currently_entitled_tiers")]
// // public CurrentlyEntitledTiers CurrentlyEntitledTiers { get; set; }
// }
public sealed class PatreonResponse
{
[JsonPropertyName("data")]
public List<PatreonMember> Data { get; set; }
[JsonPropertyName("included")]
public List<PatreonUser> Included { get; set; }
[JsonPropertyName("links")]
public PatreonLinks Links { get; set; }
}
public sealed class PatreonLinks
{
[JsonPropertyName("next")]
public string Next { get; set; }
}
public sealed class PatreonUser
{
[JsonPropertyName("attributes")]
public PatreonUserAttributes Attributes { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
// public string Type { get; set; }
}
public sealed class PatreonUserAttributes
{
[JsonPropertyName("social_connections")]
public PatreonSocials SocialConnections { get; set; }
}
public sealed class PatreonSocials
{
[JsonPropertyName("discord")]
public DiscordSocial Discord { get; set; }
}
public sealed class DiscordSocial
{
[JsonPropertyName("user_id")]
public string UserId { get; set; }
}
public sealed class PatreonMember
{
[JsonPropertyName("attributes")]
public Attributes Attributes { get; set; }
[JsonPropertyName("relationships")]
public Relationships Relationships { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
}
public sealed class Relationships
{
[JsonPropertyName("user")]
public PatreonRelationshipUser User { get; set; }
}
public sealed class PatreonRelationshipUser
{
[JsonPropertyName("data")]
public PatreonUserData Data { get; set; }
}
public sealed class PatreonUserData
{
[JsonPropertyName("id")]
public string Id { get; set; }
}
}

View File

@@ -1,62 +0,0 @@
namespace NadekoBot.Modules.Utility.Common.Patreon
{
public class Attributes
{
public int amount_cents { get; set; }
public string created_at { get; set; }
public object declined_since { get; set; }
public bool is_twitch_pledge { get; set; }
public bool patron_pays_fees { get; set; }
public int? pledge_cap_cents { get; set; }
}
public class Address
{
public object data { get; set; }
}
public class Data
{
public string id { get; set; }
public string type { get; set; }
}
public class Links
{
public string related { get; set; }
}
public class Creator
{
public Data data { get; set; }
public Links links { get; set; }
}
public class Patron
{
public Data data { get; set; }
public Links links { get; set; }
}
public class Reward
{
public Data data { get; set; }
public Links links { get; set; }
}
public class Relationships
{
public Address address { get; set; }
public Creator creator { get; set; }
public Patron patron { get; set; }
public Reward reward { get; set; }
}
public class PatreonPledge
{
public Attributes attributes { get; set; }
public string id { get; set; }
public Relationships relationships { get; set; }
public string type { get; set; }
}
}

View File

@@ -1,64 +0,0 @@
namespace NadekoBot.Modules.Utility.Common.Patreon
{
public class DiscordConnection
{
public string user_id { get; set; }
}
public class SocialConnections
{
public object deviantart { get; set; }
public DiscordConnection discord { get; set; }
public object facebook { get; set; }
public object spotify { get; set; }
public object twitch { get; set; }
public object twitter { get; set; }
public object youtube { get; set; }
}
public class UserAttributes
{
public string about { get; set; }
public string created { get; set; }
public object discord_id { get; set; }
public string email { get; set; }
public object facebook { get; set; }
public object facebook_id { get; set; }
public string first_name { get; set; }
public string full_name { get; set; }
public int gender { get; set; }
public bool has_password { get; set; }
public string image_url { get; set; }
public bool is_deleted { get; set; }
public bool is_nuked { get; set; }
public bool is_suspended { get; set; }
public string last_name { get; set; }
public SocialConnections social_connections { get; set; }
public int status { get; set; }
public string thumb_url { get; set; }
public object twitch { get; set; }
public string twitter { get; set; }
public string url { get; set; }
public string vanity { get; set; }
public object youtube { get; set; }
}
public class Campaign
{
public Data data { get; set; }
public Links links { get; set; }
}
public class UserRelationships
{
public Campaign campaign { get; set; }
}
public class PatreonUser
{
public UserAttributes attributes { get; set; }
public string id { get; set; }
public UserRelationships relationships { get; set; }
public string type { get; set; }
}
}

View File

@@ -6,6 +6,7 @@ using NadekoBot.Extensions;
using Discord;
using NadekoBot.Common.Attributes;
using NadekoBot.Modules.Utility.Services;
using Serilog;
namespace NadekoBot.Modules.Utility
{
@@ -25,8 +26,12 @@ namespace NadekoBot.Modules.Utility
[RequireContext(ContextType.DM)]
public async Task ClaimPatreonRewards()
{
if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
if (string.IsNullOrWhiteSpace(_creds.Patreon.AccessToken))
{
Log.Warning("In order to use patreon reward commands, " +
"you need to specify CampaignId and AccessToken in creds.yml");
return;
}
if (DateTime.UtcNow.Day < 5)
{

View File

@@ -2,17 +2,21 @@
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Utility.Common.Patreon;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Discord;
using NadekoBot.Modules.Gambling.Services;
using NadekoBot.Extensions;
using Serilog;
using StackExchange.Redis;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace NadekoBot.Modules.Utility.Services
{
@@ -20,96 +24,195 @@ namespace NadekoBot.Modules.Utility.Services
{
private readonly SemaphoreSlim getPledgesLocker = new SemaphoreSlim(1, 1);
private PatreonUserAndReward[] _pledges;
private readonly Timer _updater;
private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1);
public TimeSpan Interval { get; } = TimeSpan.FromMinutes(3);
private readonly IBotCredentials _creds;
private readonly DbService _db;
private readonly ICurrencyService _currency;
private readonly GamblingConfigService _gamblingConfigService;
private readonly ConnectionMultiplexer _redis;
private readonly IBotCredsProvider _credsProvider;
private readonly IHttpClientFactory _httpFactory;
private readonly IEmbedBuilderService _eb;
private readonly DiscordSocketClient _client;
public DateTime LastUpdate { get; private set; } = DateTime.UtcNow;
public PatreonRewardsService(IBotCredentials creds, DbService db,
ICurrencyService currency, IHttpClientFactory factory, IEmbedBuilderService eb,
DiscordSocketClient client, GamblingConfigService gamblingConfigService)
public PatreonRewardsService(
DbService db,
ICurrencyService currency,
IHttpClientFactory factory,
IEmbedBuilderService eb,
DiscordSocketClient client,
GamblingConfigService gamblingConfigService,
ConnectionMultiplexer redis,
IBotCredsProvider credsProvider)
{
_creds = creds;
_db = db;
_currency = currency;
_gamblingConfigService = gamblingConfigService;
_redis = redis;
_credsProvider = credsProvider;
_httpFactory = factory;
_eb = eb;
_client = client;
if (client.ShardId == 0)
_updater = new Timer(async _ => await RefreshPledges().ConfigureAwait(false),
_updater = new Timer(async _ => await RefreshPledges(_credsProvider.GetCreds()).ConfigureAwait(false),
null, TimeSpan.Zero, Interval);
}
public async Task RefreshPledges()
private DateTime LastAccessTokenUpdate(IBotCredentials creds)
{
if (string.IsNullOrWhiteSpace(_creds.PatreonAccessToken)
|| string.IsNullOrWhiteSpace(_creds.PatreonAccessToken))
return;
var db = _redis.GetDatabase();
var val = db.StringGet($"{creds.RedisKey()}_patreon_update");
if (val == default)
return DateTime.MinValue;
var lastTime = DateTime.FromBinary((long)val);
return lastTime;
}
private sealed class PatreonRefreshData
{
[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
[JsonPropertyName("refresh_token")]
public string RefreshToken { get; set; }
[JsonPropertyName("expires_in")]
public long ExpiresIn { get; set; }
[JsonPropertyName("scope")]
public string Scope { get; set; }
[JsonPropertyName("token_type")]
public string TokenType { get; set; }
}
private async Task<bool> UpdateAccessToken(IBotCredentials creds)
{
Log.Information("Updating patreon access token...");
try
{
using var http = _httpFactory.CreateClient();
var res = await http.PostAsync($"https://www.patreon.com/api/oauth2/token" +
$"?grant_type=refresh_token" +
$"&refresh_token={creds.Patreon.RefreshToken}" +
$"&client_id={creds.Patreon.ClientId}" +
$"&client_secret={creds.Patreon.ClientSecret}",
new StringContent(string.Empty));
res.EnsureSuccessStatusCode();
var data = await res.Content.ReadFromJsonAsync<PatreonRefreshData>();
if (data is null)
throw new("Invalid patreon response.");
_credsProvider.ModifyCredsFile(oldData =>
{
oldData.Patreon.AccessToken = data.AccessToken;
oldData.Patreon.RefreshToken = data.RefreshToken;
});
var db = _redis.GetDatabase();
await db.StringSetAsync($"{creds.RedisKey()}_patreon_update", DateTime.UtcNow.ToBinary());
return true;
}
catch (Exception ex)
{
Log.Error("Failed updating patreon access token: {ErrorMessage}", ex.ToString());
return false;
}
}
private bool HasPatreonCreds(IBotCredentials creds)
{
var _1 = creds.Patreon.ClientId;
var _2 = creds.Patreon.ClientSecret;
var _4 = creds.Patreon.RefreshToken;
return !(string.IsNullOrWhiteSpace(_1)
|| string.IsNullOrWhiteSpace(_2)
|| string.IsNullOrWhiteSpace(_4));
}
public async Task RefreshPledges(IBotCredentials creds)
{
if (DateTime.UtcNow.Day < 5)
return;
// if the user has the necessary patreon creds
// and the access token expired or doesn't exist
// -> update access token
if (!HasPatreonCreds(creds))
return;
if (LastAccessTokenUpdate(creds).Month < DateTime.UtcNow.Month
|| string.IsNullOrWhiteSpace(creds.Patreon.AccessToken))
{
var success = await UpdateAccessToken(creds);
if (!success)
return;
}
LastUpdate = DateTime.UtcNow;
await getPledgesLocker.WaitAsync().ConfigureAwait(false);
try
{
var rewards = new List<PatreonPledge>();
var members = new List<PatreonMember>();
var users = new List<PatreonUser>();
using (var http = _httpFactory.CreateClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken);
var data = new PatreonData()
{
Links = new PatreonDataLinks()
{
next = $"https://api.patreon.com/oauth2/api/campaigns/{_creds.PatreonCampaignId}/pledges"
}
};
http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization",
$"Bearer {creds.Patreon.AccessToken}");
var page = $"https://www.patreon.com/api/oauth2/v2/campaigns/{creds.Patreon.CampaignId}/members" +
"?fields%5Bmember%5D=full_name,currently_entitled_amount_cents" +
"&fields%5Buser%5D=social_connections" +
"&include=user";
PatreonResponse data = null;
do
{
var res = await http.GetStringAsync(data.Links.next)
.ConfigureAwait(false);
data = JsonConvert.DeserializeObject<PatreonData>(res);
var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge");
rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject<PatreonPledge>(x.ToString()))
.Where(x => x.attributes.declined_since is null));
if (data.Included != null)
{
users.AddRange(data.Included
.Where(x => x["type"].ToString() == "user")
.Select(x => JsonConvert.DeserializeObject<PatreonUser>(x.ToString())));
}
} while (!string.IsNullOrWhiteSpace(data.Links.next));
var res = await http.GetStringAsync(page).ConfigureAwait(false);
data = JsonSerializer.Deserialize<PatreonResponse>(res);
if (data is null)
break;
members.AddRange(data.Data);
users.AddRange(data.Included);
} while (!string.IsNullOrWhiteSpace(page = data?.Links?.Next));
}
var toSet = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward()
{
User = y,
Reward = x,
}).ToArray();
_pledges = toSet;
foreach (var pledge in _pledges)
{
var userIdStr = pledge.User.attributes?.social_connections?.discord?.user_id;
if (userIdStr != null && ulong.TryParse(userIdStr, out var userId))
var userData = members.Join(users,
(m) => m.Relationships.User.Data.Id,
(u) => u.Id,
(m, u) => new
{
await ClaimReward(userId);
}
PatreonUserId = m.Relationships.User.Data.Id,
UserId = ulong.TryParse(u.Attributes?.SocialConnections?.Discord?.UserId ?? string.Empty,
out var userId)
? userId
: 0,
EntitledTo = m.Attributes.CurrentlyEntitledAmountCents,
})
.Where(x => x is
{
UserId: not 0,
EntitledTo: > 0
})
.ToList();
foreach (var pledge in userData)
{
await ClaimReward(pledge.UserId, pledge.PatreonUserId, pledge.EntitledTo);
}
}
catch (Exception ex)
@@ -123,80 +226,73 @@ namespace NadekoBot.Modules.Utility.Services
}
public async Task<int> ClaimReward(ulong userId)
public async Task<int> ClaimReward(ulong userId, string patreonUserId, int cents)
{
await claimLockJustInCase.WaitAsync().ConfigureAwait(false);
var settings = _gamblingConfigService.Data;
var now = DateTime.UtcNow;
try
{
var datas = _pledges?.Where(x => x.User.attributes?.social_connections?.discord?.user_id == userId.ToString())
?? Enumerable.Empty<PatreonUserAndReward>();
var eligibleFor = (int)(cents * settings.PatreonCurrencyPerCent);
var totalAmount = 0;
foreach (var data in datas)
using (var uow = _db.GetDbContext())
{
var amount = (int)(data.Reward.attributes.amount_cents * settings.PatreonCurrencyPerCent);
var users = uow.Set<RewardedUser>();
var usr = await users.FirstOrDefaultAsync(x => x.PatreonUserId == patreonUserId);
using (var uow = _db.GetDbContext())
if (usr is null)
{
var users = uow.Set<RewardedUser>();
var usr = users.FirstOrDefault(x => x.PatreonUserId == data.User.id);
if (usr is null)
users.Add(new RewardedUser()
{
users.Add(new RewardedUser()
{
PatreonUserId = data.User.id,
LastReward = now,
AmountRewardedThisMonth = amount,
});
PatreonUserId = patreonUserId,
LastReward = now,
AmountRewardedThisMonth = eligibleFor,
});
await uow.SaveChangesAsync();
await uow.SaveChangesAsync();
await _currency.AddAsync(userId, "Patreon reward - new", amount, gamble: true);
totalAmount += amount;
Log.Information($"Sending new currency reward to {userId}");
await SendMessageToUser(userId, $"Thank you for your pledge! " +
$"You've been awarded **{amount}**{settings.Currency.Sign} !");
continue;
}
if (usr.LastReward.Month != now.Month)
{
usr.LastReward = now;
usr.AmountRewardedThisMonth = amount;
await uow.SaveChangesAsync();
await _currency.AddAsync(userId, "Patreon reward - recurring", amount, gamble: true);
totalAmount += amount;
Log.Information($"Sending recurring currency reward to {userId}");
await SendMessageToUser(userId, $"Thank you for your continued support! " +
$"You've been awarded **{amount}**{settings.Currency.Sign} for this month's support!");
continue;
}
if (usr.AmountRewardedThisMonth < amount)
{
var toAward = amount - usr.AmountRewardedThisMonth;
usr.LastReward = now;
usr.AmountRewardedThisMonth = amount;
await uow.SaveChangesAsync();
await _currency.AddAsync(userId, "Patreon reward - update", toAward, gamble: true);
totalAmount += toAward;
Log.Information($"Sending updated currency reward to {userId}");
await SendMessageToUser(userId, $"Thank you for increasing your pledge! " +
$"You've been awarded an additional **{toAward}**{settings.Currency.Sign} !");
continue;
}
await _currency.AddAsync(userId, "Patreon reward - new", eligibleFor, gamble: true);
Log.Information($"Sending new currency reward to {userId}");
await SendMessageToUser(userId, $"Thank you for your pledge! " +
$"You've been awarded **{eligibleFor}**{settings.Currency.Sign} !");
return eligibleFor;
}
}
return totalAmount;
if (usr.LastReward.Month != now.Month)
{
usr.LastReward = now;
usr.AmountRewardedThisMonth = eligibleFor;
await uow.SaveChangesAsync();
await _currency.AddAsync(userId, "Patreon reward - recurring", eligibleFor, gamble: true);
Log.Information($"Sending recurring currency reward to {userId}");
await SendMessageToUser(userId, $"Thank you for your continued support! " +
$"You've been awarded **{eligibleFor}**{settings.Currency.Sign} for this month's support!");
return eligibleFor;
}
if (usr.AmountRewardedThisMonth < eligibleFor)
{
var toAward = eligibleFor - usr.AmountRewardedThisMonth;
usr.LastReward = now;
usr.AmountRewardedThisMonth = toAward;
await uow.SaveChangesAsync();
await _currency.AddAsync(userId, "Patreon reward - update", toAward, gamble: true);
Log.Information($"Sending updated currency reward to {userId}");
await SendMessageToUser(userId, $"Thank you for increasing your pledge! " +
$"You've been awarded an additional **{toAward}**{settings.Currency.Sign} !");
return toAward;
}
return 0;
}
}
finally
{

View File

@@ -10,7 +10,14 @@ using Serilog;
namespace NadekoBot.Services
{
public sealed class BotCredsProvider
public interface IBotCredsProvider
{
public void Reload();
public IBotCredentials GetCreds();
public void ModifyCredsFile(Action<Creds> func);
}
public sealed class BotCredsProvider : IBotCredsProvider
{
private readonly int? _totalShards;
private const string _credsFileName = "creds.yml";
@@ -27,7 +34,7 @@ namespace NadekoBot.Services
private readonly object reloadLock = new object();
private void Reload()
public void Reload()
{
lock (reloadLock)
{
@@ -102,6 +109,19 @@ namespace NadekoBot.Services
Reload();
}
public void ModifyCredsFile(Action<Creds> func)
{
var ymlData = File.ReadAllText(_credsFileName);
var creds = Yaml.Deserializer.Deserialize<Creds>(ymlData);
func(creds);
ymlData = Yaml.Serializer.Serialize(creds);
File.WriteAllText(_credsFileName, ymlData);
Reload();
}
/// <summary>
/// Checks if there's a V2 credentials file present, loads it if it exists,
/// converts it to new model, and saves it to YAML. Also backs up old credentials to credentials.json.bak
@@ -157,6 +177,6 @@ namespace NadekoBot.Services
}
public Creds GetCreds() => _creds;
public IBotCredentials GetCreds() => _creds;
}
}

View File

@@ -31,11 +31,9 @@ votes:
# 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
clientId:
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: ''