Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discordへの試合結果の送信を自動送信からボタン操作による送信に変更 #61

Draft
wants to merge 5 commits into
base: H-dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Modules/CustomPalette.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ public static class CustomPalette
{
public static readonly Color32 EnabledGreen = new(0, 255, 127, 255);
public static readonly Color32 DisabledRed = new(255, 99, 71, 255);
public static readonly Color32 SucceededBlue = new(0, 144, 255, 255);
public static readonly Color32 FailedRed = new(255, 0, 148, 255);
}
}
127 changes: 93 additions & 34 deletions Modules/Webhook/WebhookManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,31 @@ public sealed class WebhookManager : IDisposable
private static readonly ILogHandler logger = Logger.Handler(nameof(WebhookManager));
private bool disposedValue;

public void StartSend(WebhookMessageBuilder builder)
public void StartSend(WebhookMessageBuilder builder, Action<OnCompleteArgs> onComplete = default)
{
if (!TryReadUrl(out var url))
{
logger.Warn("URL設定が正しくありません");
return;
}
var sendTask = SendAsync(builder, url);
sendTask.ContinueWith(task =>
try
{
if (task.Exception is { } aggregateException)
if (!TryReadUrl(out var url))
{
logger.Warn("送信中に例外が発生しました");
logger.Exception(aggregateException.InnerException);
logger.Warn("URL設定が正しくありません");
onComplete?.Invoke(new(true, FailureReason.InvalidUrl));
return;
}
});
var sendTask = SendAsync(builder, url, onComplete);
sendTask.ContinueWith(task =>
{
if (task.Exception is { } aggregateException)
{
logger.Warn("送信中に例外が発生しました");
logger.Exception(aggregateException.InnerException);
}
});
}
catch
{
onComplete?.Invoke(new(true, FailureReason.Exception));
throw;
}
}
private bool TryReadUrl(out string url)
{
Expand Down Expand Up @@ -79,39 +88,71 @@ private bool ValidateUrl(string url)
}
return webhookUrlRegex.IsMatch(url);
}
public async Task SendAsync(WebhookMessageBuilder builder, string url, CancellationToken cancellationToken = default)
public async Task SendAsync(WebhookMessageBuilder builder, string url, Action<OnCompleteArgs> onComplete = default, CancellationToken cancellationToken = default)
{
var fullMessage = builder.ContentBuilder.ToString();
if (fullMessage.Length <= MaxContentLength)
{
await SendInnerAsync(fullMessage, builder.UserName, builder.AvatarUrl, url, cancellationToken);
return;
}
// 改行を区切りとして,上限文字数を超えないように分割して送信する
// 1行で上限を超えているケースは考慮しない
var lines = fullMessage.Split(Environment.NewLine);
var partBuilder = new StringBuilder();
foreach (var line in lines)
try
{
if (partBuilder.Length + line.Length > MaxContentLength)
var fullMessage = builder.ContentBuilder.ToString();
if (fullMessage.Length <= MaxContentLength)
{
if (await SendInnerAsync(fullMessage, builder.UserName, builder.AvatarUrl, url, cancellationToken))
{
onComplete?.Invoke(new(false));
}
else
{
onComplete?.Invoke(new(true, FailureReason.Network));
}
return;
}

var hasFailure = false;
// 改行を区切りとして,上限文字数を超えないように分割して送信する
// 1行で上限を超えているケースは考慮しない
var lines = fullMessage.Split(Environment.NewLine);
var partBuilder = new StringBuilder();
foreach (var line in lines)
{
if (partBuilder.Length + line.Length > MaxContentLength)
{
if (!await SendInnerAsync(partBuilder.ToString(), builder.UserName, builder.AvatarUrl, url, cancellationToken))
{
hasFailure = true;
}
partBuilder.Clear();
await Task.Delay(1000, cancellationToken);
}
partBuilder.AppendLine(line);
}
if (partBuilder.Length > 0)
{
await SendInnerAsync(partBuilder.ToString(), builder.UserName, builder.AvatarUrl, url, cancellationToken);
partBuilder.Clear();
await Task.Delay(1000, cancellationToken);
if (!await SendInnerAsync(partBuilder.ToString(), builder.UserName, builder.AvatarUrl, url, cancellationToken))
{
hasFailure = true;
}
}

if (hasFailure)
{
onComplete?.Invoke(new(true, FailureReason.Network));
}
else
{
onComplete?.Invoke(new(false));
}
partBuilder.AppendLine(line);
}
if (partBuilder.Length > 0)
catch
{
await SendInnerAsync(partBuilder.ToString(), builder.UserName, builder.AvatarUrl, url, cancellationToken);
onComplete?.Invoke(new(true, FailureReason.Exception));
throw;
}
}
private async Task SendInnerAsync(string message, string userName, string avatarUrl, string url, CancellationToken cancellationToken = default)
private async Task<bool> SendInnerAsync(string message, string userName, string avatarUrl, string url, CancellationToken cancellationToken = default)
{
var content = new WebhookRequest(message, userName, avatarUrl);
await SendAsync(content, url, cancellationToken);
return await SendAsync(content, url, cancellationToken);
}
private async Task SendAsync(WebhookRequest webhookRequest, string url, CancellationToken cancellationToken = default)
private async Task<bool> SendAsync(WebhookRequest webhookRequest, string url, CancellationToken cancellationToken = default)
{
try
{
Expand All @@ -120,12 +161,15 @@ private async Task SendAsync(WebhookRequest webhookRequest, string url, Cancella
if (!response.IsSuccessStatusCode)
{
logger.Warn("送信に失敗");
return false;
}
return true;
}
catch (TaskCanceledException taskCanceledException)
{
logger.Warn("送信はキャンセルされました");
logger.Exception(taskCanceledException);
return false;
}
}

Expand All @@ -147,6 +191,21 @@ public void Dispose()
GC.SuppressFinalize(this);
}

public readonly struct OnCompleteArgs(bool hasFailure, FailureReason failureReason = default)
{
public bool HasFailure { get; } = hasFailure;
public FailureReason FailureReason { get; } = failureReason;
}
public readonly struct FailureReason(string message)
{
public string Message { get; } = message;

public static FailureReason InvalidUrl { get; } = new("URL設定が正しくありません。設定を確認してください");
public static FailureReason Exception { get; } = new("処理中にエラーが発生しました");
public static FailureReason Network { get; } = new("送信を完了できませんでした。\nネットワーク品質、Modの不具合、Discord側の問題が原因の可能性があります");

}

public FileInfo WebhookUrlFile { get; } =
#if DEBUG
new("DebugWebhookUrl.txt");
Expand Down
91 changes: 91 additions & 0 deletions Objects/FlatButton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using TMPro;
using UnityEngine;
using Object = UnityEngine.Object;

namespace TownOfHost.Objects;

public sealed class FlatButton
{
public FlatButton(Transform parent, string name, Vector3 localPosition, Color32 normalColor, Color32 hoverColor, Action action, string label, Vector2 scale)
{
Button = Object.Instantiate(buttonPrefab, parent);
Label = Button.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
NormalRenderer = Button.inactiveSprites.GetComponent<SpriteRenderer>();
HoverRenderer = Button.activeSprites.GetComponent<SpriteRenderer>();
ButtonCollider = Button.GetComponent<BoxCollider2D>();

var container = Label.transform.parent;
Object.Destroy(Label.GetComponent<AspectPosition>());
container.SetLocalX(0f);
Label.transform.SetLocalX(0f);
Label.horizontalAlignment = HorizontalAlignmentOptions.Center;

NormalRenderer.color = normalColor;
HoverRenderer.color = hoverColor;

Button.name = name;
Button.transform.localPosition = localPosition;
Button.OnClick.AddListener(action);
Label.text = label;
Scale = scale;
Button.gameObject.SetActive(true);
}

public PassiveButton Button { get; }
public TextMeshPro Label { get; }
public SpriteRenderer NormalRenderer { get; }
public SpriteRenderer HoverRenderer { get; }
public BoxCollider2D ButtonCollider { get; }
private Vector2 _scale;
public Vector2 Scale
{
get => _scale;
set => _scale = NormalRenderer.size = HoverRenderer.size = ButtonCollider.size = value;
}
private float _fontSize;
public float FontSize
{
get => _fontSize;
set => _fontSize = Label.fontSize = Label.fontSizeMin = Label.fontSizeMax = value;
}

private static PassiveButton buttonPrefab = CreatePrefab();
private static PassiveButton CreatePrefab()
{
if (Prefabs.SimpleButton == null)
{
throw new InvalidOperationException("SimpleButtonのプレファブが未設定");
}

var button = Object.Instantiate(Prefabs.SimpleButton);
var label = button.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
var normalRenderer = button.inactiveSprites.GetComponent<SpriteRenderer>();
var hoverRenderer = button.activeSprites.GetComponent<SpriteRenderer>();

Object.Destroy(button.GetComponent<AspectScaledAsset>());

// 初回だけちょっと重いのどうにかしたい
var texture = new Texture2D(normalRenderer.sprite.texture.width, normalRenderer.sprite.texture.height, TextureFormat.ARGB32, false);
for (var x = 0; x < texture.width; x++)
{
for (var y = 0; y < texture.height; y++)
{
texture.SetPixel(x, y, Color.white);
}
}
texture.Apply();
var normalSprite = Sprite.Create(texture, new(0f, 0f, texture.width, texture.height), new(0.5f, 0.5f));
var hoverSprite = Sprite.Create(texture, new(0f, 0f, texture.width, texture.height), new(0.5f, 0.5f));
normalRenderer.sprite = normalSprite;
hoverRenderer.sprite = hoverSprite;

button.name = "FlatButtonPrefab";
label.text = "Flat Button Prefab";
button.gameObject.SetActive(false);
Object.DontDestroyOnLoad(button);
return button;
}

public static bool IsNullOrDestroyed(FlatButton flatButton) => flatButton == null || flatButton.Button == null;
}
25 changes: 25 additions & 0 deletions Objects/Prefabs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,30 @@ public static TextMeshPro SimpleText
}
}
}
private static PassiveButton _simpleButton;
public static PassiveButton SimpleButton
{
get => _simpleButton;
set
{
if (_simpleButton != null)
{
return;
}
_simpleButton = Object.Instantiate(value);
var label = _simpleButton.transform.Find("FontPlacer/Text_TMP").GetComponent<TextMeshPro>();
_simpleButton.gameObject.SetActive(false);
Object.DontDestroyOnLoad(_simpleButton);
_simpleButton.name = "SimpleButtonPrefab";
Object.Destroy(_simpleButton.GetComponent<AspectPosition>());
label.DestroyTranslator();
label.fontSize = label.fontSizeMax = label.fontSizeMin = 3.5f;
label.enableWordWrapping = false;
label.text = "Simple Button Prefab";
var buttonCollider = _simpleButton.GetComponent<BoxCollider2D>();
buttonCollider.offset = new(0f, 0f);
_simpleButton.OnClick = new();
}
}
}
}
Loading