Skip to content

Commit

Permalink
Purchase: 課金額を記録する機能を追加 (#19)
Browse files Browse the repository at this point in the history
* Common: EFCore追加

* Purchase: 課金額を記録する機能を追加

* コンテナ周り整備
  • Loading branch information
2RiniaR authored Nov 3, 2024
1 parent 1290ea6 commit e9ff8a5
Show file tree
Hide file tree
Showing 24 changed files with 354 additions and 24 deletions.
15 changes: 15 additions & 0 deletions .idea/.idea.king-server.dir/.idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Common/EnvironmentManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class EnvironmentManager
public static ulong DiscordMainChannelId => ulong.Parse(Get("DiscordMainChannelId"));
public static string GoogleCredentialFilePath => Get("GoogleCredentialFilePath");
public static string GoogleMasterSheetId => Get("GoogleMasterSheetId");
public static string SqliteConnectionString => Get("SqliteConnectionString");

private static string Get(string name)
{
Expand Down
15 changes: 9 additions & 6 deletions Common/GachaManager.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
namespace Approvers.King.Common;

public class GachaManager
public class GachaManager : Singleton<GachaManager>
{
private readonly List<ReplyMessage> _replyMessageTable = new();

public static GachaManager Instance { get; } = new();

/// <summary>
/// 現在のメッセージに反応する確率
/// </summary>
Expand All @@ -26,13 +24,18 @@ public void Initialize()
}));
}

public string? TryPickRareReplyMessage()
public string? Roll()
{
if (RandomUtility.IsHit(RareReplyRate) == false) return null;
return PickMessage();
return GetRandomResult();
}

public string RollWithoutNone()
{
return GetRandomResult();
}

public string PickMessage()
private string GetRandomResult()
{
var totalRate = _replyMessageTable.Sum(x => x.Rate);
var value = RandomUtility.GetRandomFloat(totalRate);
Expand Down
4 changes: 4 additions & 0 deletions Common/Master/SettingMaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@ private int GetInt(string key)
public int TypingMaxDuration => GetInt(nameof(TypingMaxDuration));
public int SilentDuration => GetInt(nameof(SilentDuration));
public int DailyResetTime => GetInt(nameof(DailyResetTime));
public int MonthlyResetDay => GetInt(nameof(MonthlyResetDay));
public int BirthdayMonth => GetInt(nameof(BirthdayMonth));
public int BirthdayDay => GetInt(nameof(BirthdayDay));
public int MaxRareReplyProbabilityPermillage => GetInt(nameof(MaxRareReplyProbabilityPermillage));
public int RareReplyProbabilityStepPermillage => GetInt(nameof(RareReplyProbabilityStepPermillage));
public int PricePerGachaOnce => GetInt(nameof(PricePerGachaOnce));
public int PricePerGachaTenTimes => GetInt(nameof(PricePerGachaTenTimes));
public int PricePerGachaOnceCertain => GetInt(nameof(PricePerGachaOnceCertain));
public string SilentReplyMessage => GetString(nameof(SilentReplyMessage));
}

Expand Down
31 changes: 31 additions & 0 deletions Common/Models/AppService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;

namespace Approvers.King.Common;

public class AppService : DbContext
{
public DbSet<User> Users { get; set; }

public static AppService CreateSession()
{
return new AppService();
}

protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlite(EnvironmentManager.SqliteConnectionString);
}

public async Task<User> FindOrCreateUserAsync(ulong discordId)
{
var user = await Users.FindAsync(discordId);
if (user != null)
{
return user;
}

user = new User { DiscordID = discordId };
Add(user);
return user;
}
}
36 changes: 36 additions & 0 deletions Common/Models/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace Approvers.King.Common;

public class User
{
[Key]
public ulong DiscordID { get; set; }
public int MonthlyPurchase { get; set; }

public void ResetMonthlyPurchase()
{
MonthlyPurchase = 0;
}

public string? RollGachaOnce()
{
MonthlyPurchase += MasterManager.SettingMaster.PricePerGachaOnce;
return GachaManager.Instance.Roll();
}

public string RollGachaOnceCertain()
{
MonthlyPurchase += MasterManager.SettingMaster.PricePerGachaOnceCertain;
return GachaManager.Instance.RollWithoutNone();
}

public List<string?> RollGachaTenTimes()
{
const int pickCount = 10;
MonthlyPurchase += MasterManager.SettingMaster.PricePerGachaTenTimes;
return Enumerable.Range(0, pickCount).Select(_ => GachaManager.Instance.Roll()).ToList();
}
}
5 changes: 5 additions & 0 deletions Common/NumberUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ namespace Approvers.King.Common;

public static class NumberUtility
{
public static DateTime GetBaseDateTime()
{
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}

public static float GetSecondsFromMilliseconds(float milliseconds)
{
return milliseconds / 1000f;
Expand Down
2 changes: 1 addition & 1 deletion Common/RandomUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public static class RandomUtility
{
private static readonly Random Random = new();
private static readonly Random Random = new((int)DateTime.Now.Ticks & 0x0000FFFF);

public static float GetRandomFloat(float max)
{
Expand Down
11 changes: 11 additions & 0 deletions Common/Scheduler/SchedulerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ private static void OnEverySecond(object? sender, ElapsedEventArgs e)
});
}

public static void RegisterMonthly<T>(int day, TimeSpan time) where T : SchedulerJobPresenterBase, new()
{
Runners.Add(new SchedulerJobRunner<T>
{
Predicate = x => x.Day == day &&
x.Hour == time.Hours &&
x.Minute == time.Minutes &&
x.Second == time.Seconds
});
}

public static void RegisterYearly<T>(DateTime datetime) where T : SchedulerJobPresenterBase, new()
{
Runners.Add(new SchedulerJobRunner<T>
Expand Down
2 changes: 1 addition & 1 deletion Common/TimeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public static class TimeManager
{
public static TimeSpan DailyResetTime => TimeSpan.FromMilliseconds(MasterManager.SettingMaster.DailyResetTime);

public static int MonthlyResetDay => MasterManager.SettingMaster.MonthlyResetDay;
public static DateTime Birthday => new DateTime(1, MasterManager.SettingMaster.BirthdayMonth,
MasterManager.SettingMaster.BirthdayDay);

Expand Down
6 changes: 5 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ WORKDIR /App
COPY . .
RUN dotnet restore
RUN dotnet publish -c Release -o out
ENV PATH="$PATH:/root/.dotnet/tools"
RUN dotnet tool install --global dotnet-ef --version 8.0.10
RUN dotnet ef migrations bundle -o out/efbundle --self-contained -r linux-x64
RUN chmod 755 out/efbundle

FROM mcr.microsoft.com/dotnet/runtime:8.0 AS runtime
FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/runtime:8.0 AS runtime
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "king-server.dll"]
3 changes: 1 addition & 2 deletions Environment/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ services:
app:
image: [ docker_user_id ]/king-server:latest
environment:
DISCORD_SECRET: SECRET # Change this
TZ: Asia/Tokyo
TZ: Asia/Tokyo
24 changes: 16 additions & 8 deletions Events/GachaCommandPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ namespace Approvers.King.Events;

public class GachaCommandPresenter : DiscordMessagePresenterBase
{
private const int PickCount = 10;

protected override async Task MainAsync()
{
var results = Enumerable.Range(0, PickCount)
.Select(_ => GachaManager.Instance.TryPickRareReplyMessage())
.ToList();
await using var app = AppService.CreateSession();
var user = await app.FindOrCreateUserAsync(Message.Author.Id);

var results = user.RollGachaTenTimes();
await SendReplyAsync(user, results);

await app.SaveChangesAsync();
}

private async Task SendReplyAsync(User user, IReadOnlyList<string?> results)
{
var builder = new StringBuilder();
builder.AppendLine($"↓↓↓ いっそう{PickCount}連おみくじ ↓↓↓");
builder.AppendLine($"↓↓↓ いっそう{results.Count}連おみくじ ↓↓↓");
foreach (var result in results)
{
builder.AppendLine(result != null ? Format.Bold($"・{result}") : Format.Code("x"));
Expand All @@ -24,10 +29,13 @@ protected override async Task MainAsync()
if (results.All(x => x == null))
{
builder.AppendLine();
var ridiculeMessage = MasterManager.RandomMessageMaster.GetAll(x => x.Type == RandomMessageType.GachaFailed).PickRandom().Content;
builder.AppendLine(Format.Bold(Format.Italics(ridiculeMessage)));
var failedMessage = MasterManager.RandomMessageMaster.GetAll(x => x.Type == RandomMessageType.GachaFailed).PickRandom().Content;
builder.AppendLine(Format.Bold(Format.Italics(failedMessage)));
}

builder.AppendLine();
builder.AppendLine($"おまえの今月の課金額 → {user.MonthlyPurchase:N0}†カス†(税込)");

await Message.ReplyAsync(builder.ToString());
}
}
13 changes: 12 additions & 1 deletion Events/InteractReplyPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ namespace Approvers.King.Events;
public class InteractReplyPresenter : DiscordMessagePresenterBase
{
protected override async Task MainAsync()
{
await using var app = AppService.CreateSession();
var user = await app.FindOrCreateUserAsync(Message.Author.Id);

var message = user.RollGachaOnceCertain();
await SendReplyAsync(message);

await app.SaveChangesAsync();
}

private async Task SendReplyAsync(string message)
{
var replyMaxDelay = NumberUtility.GetSecondsFromMilliseconds(MasterManager.SettingMaster.ReplyMaxDuration);
await Task.Delay(TimeSpan.FromSeconds(RandomUtility.GetRandomFloat(replyMaxDelay)));
using (Message.Channel.EnterTypingState())
{
var typingMaxDelay = NumberUtility.GetSecondsFromMilliseconds(MasterManager.SettingMaster.TypingMaxDuration);
await Task.Delay(TimeSpan.FromSeconds(RandomUtility.GetRandomFloat(typingMaxDelay)));
await Message.ReplyAsync(GachaManager.Instance.PickMessage());
await Message.ReplyAsync(message);
}
}
}
17 changes: 17 additions & 0 deletions Events/MonthlyResetPresenter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Approvers.King.Common;
using Microsoft.EntityFrameworkCore;

namespace Approvers.King.Events;

public class MonthlyResetPresenter : SchedulerJobPresenterBase
{
protected override async Task MainAsync()
{
await using var app = AppService.CreateSession();

// ToDo: 課金額ランキング出したい

await app.Users.ForEachAsync(user => user.ResetMonthlyPurchase());
await app.SaveChangesAsync();
}
}
15 changes: 13 additions & 2 deletions Events/RareReplyPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,20 @@ protected override async Task MainAsync()
if (SilentManager.IsSilent(Message.Author.Id) ||
Message.Channel.Id != EnvironmentManager.DiscordMainChannelId) return;

var message = GachaManager.Instance.TryPickRareReplyMessage();
if (message == null) return;
await using var app = AppService.CreateSession();
var user = await app.FindOrCreateUserAsync(Message.Author.Id);

var message = user.RollGachaOnce();
if (message != null)
{
await SendReplyAsync(message);
}

await app.SaveChangesAsync();
}

private async Task SendReplyAsync(string message)
{
var replyMaxDelay = NumberUtility.GetSecondsFromMilliseconds(MasterManager.SettingMaster.ReplyMaxDuration);
await Task.Delay(TimeSpan.FromSeconds(RandomUtility.GetRandomFloat(replyMaxDelay)));
using (Message.Channel.EnterTypingState())
Expand Down
15 changes: 14 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
.PHONY: run
.PHONY: migrate
migrate:
dotnet ef migrations add $(name)
dotnet ef database update

.PHONY: run
run:
dotnet run

.PHONY: docker-build
docker-build:
docker build . -t $(app_name)

.PHONY: docker-push
docker-push:
docker push $(app_name)
24 changes: 24 additions & 0 deletions Migrations/20241103100518_InitialCreate.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions Migrations/20241103100518_InitialCreate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace Approvers.King.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{

}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{

}
}
}
Loading

0 comments on commit e9ff8a5

Please sign in to comment.