Skip to content

Commit

Permalink
Merge pull request #56 from MyFriendHowAreYou/webhookban
Browse files Browse the repository at this point in the history
webhook banhook
  • Loading branch information
13lackHawk authored Sep 18, 2023
2 parents 7bb7034 + 08c920d commit 8f40339
Show file tree
Hide file tree
Showing 9 changed files with 379 additions and 86 deletions.
10 changes: 9 additions & 1 deletion Content.Server/Administration/Managers/BanManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Content.Server.Chat.Managers;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Shared.GameTicking;
using Content.Shared.Database;
using Content.Shared.Players;
using Content.Shared.Players.PlayTimeTracking;
Expand All @@ -25,6 +26,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntitySystemManager _systems = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
[Dependency] private readonly IChatManager _chat = default!;
Expand Down Expand Up @@ -167,6 +169,8 @@ public async void CreateServerBan(NetUserId? target, string? targetUsername, Net
_sawmill.Info(logMessage);
_chat.SendAdminAlert(logMessage);

_entityManager.EventBus.RaiseEvent(EventSource.Local, new BanEvent(targetUsername ?? Loc.GetString("system-user"), expires, reason, severity, adminName));

// If we're not banning a player we don't care about disconnecting people
if (target == null)
return;
Expand All @@ -185,7 +189,7 @@ public async void CreateServerBan(NetUserId? target, string? targetUsername, Net
// Removing it will clutter the note list. Please also make sure that department bans are applied to roles with the same DateTimeOffset.
public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray<byte>? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan)
{
if (!_prototypeManager.TryIndex(role, out JobPrototype? _))
if (!_prototypeManager.TryIndex(role, out JobPrototype? jobPrototype))
{
throw new ArgumentException($"Invalid role '{role}'", nameof(role));
}
Expand All @@ -198,6 +202,9 @@ public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUs
}

_systems.TryGetEntitySystem(out GameTicker? ticker);
var adminName = banningAdmin == null
? Loc.GetString("system-user")
: (await _db.GetPlayerRecordByUserId(banningAdmin.Value))?.LastSeenUserName ?? Loc.GetString("system-user");
int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
var playtime = target == null ? TimeSpan.Zero : (await _db.GetPlayTimes(target.Value)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;

Expand Down Expand Up @@ -229,6 +236,7 @@ public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUs
{
SendRoleBans(target.Value);
}
_entityManager.EventBus.RaiseEvent(EventSource.Local, new JobBanEvent(targetUsername ?? Loc.GetString("system-user"), expires, jobPrototype, reason, severity, adminName));
}

public HashSet<string>? GetJobBans(NetUserId playerUserId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
using Content.Shared.CCVar;
using Content.Shared.Andromeda.CCVar;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.Roles;
using Robust.Shared;
using Robust.Shared.Configuration;
using System.Net.Http;
using System.Net;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text;

namespace Content.Server.Andromeda.BansNotifications
{

/// <summary>
/// Listen game events and send notifications to Discord
/// </summary>
/*
public interface IBansNotificationsSystem
{
void RaiseLocalBanEvent(string username, DateTimeOffset? expires, string reason, NoteSeverity severity, string adminusername);
void RaiseLocalJobBanEvent(string username, DateTimeOffset? expires, JobPrototype job, string reason, NoteSeverity severity, string adminusername);
void RaiseLocalDepartmentBanEvent(string username, DateTimeOffset? expires, DepartmentPrototype department, string reason, NoteSeverity severity, string adminusername);
}
*/

public sealed class BansNotificationsSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _config = default!;
private ISawmill _sawmill = default!;
private readonly HttpClient _httpClient = new();
private string _webhookUrl = String.Empty;
private string _serverName = String.Empty;

public override void Initialize()
{
_sawmill = Logger.GetSawmill("bans_notifications");
SubscribeLocalEvent<BanEvent>(OnBan);
SubscribeLocalEvent<JobBanEvent>(OnJobBan);
SubscribeLocalEvent<DepartmentBanEvent>(OnDepartmentBan);
_config.OnValueChanged(AndromedaCCVars.DiscordBanWebhook, value => _webhookUrl = value, true);
_config.OnValueChanged(CVars.GameHostName, value => _serverName = value, true);
}
/*
public void RaiseLocalBanEvent(string username, DateTimeOffset? expires, string reason, NoteSeverity severity, string adminusername)
{
RaiseLocalEvent(new BanEvent(username, expires, reason, severity, adminusername));
}
public void RaiseLocalJobBanEvent(string username, DateTimeOffset? expires, JobPrototype job, string reason, NoteSeverity severity, string adminusername)
{
RaiseLocalEvent(new JobBanEvent(username, expires, job, reason, severity, adminusername));
}
public void RaiseLocalDepartmentBanEvent(string username, DateTimeOffset? expires, DepartmentPrototype department, string reason, NoteSeverity severity, string adminusername)
{
RaiseLocalEvent(new DepartmentBanEvent(username, expires, department, reason, severity, adminusername));
}
*/
private async void SendDiscordMessage(WebhookPayload payload)
{
var request = await _httpClient.PostAsync(_webhookUrl,
new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));

_sawmill.Debug($"Discord webhook json: {JsonSerializer.Serialize(payload)}");

var content = await request.Content.ReadAsStringAsync();
if (!request.IsSuccessStatusCode)
{
_sawmill.Error($"Discord returned bad status code when posting message: {request.StatusCode}\nResponse: {content}");
return;
}
}

public void OnBan(BanEvent e)
{
if (String.IsNullOrEmpty(_webhookUrl))
return;

var expires = e.Expires == null ? Loc.GetString("discord-permanent") : Loc.GetString("discord-expires-at", ("date", e.Expires));
var message = Loc.GetString("discord-ban-msg",
("username", e.Username),
("expires", expires),
("reason", e.Reason));

var color = e.Severity switch
{
NoteSeverity.None => 0x6aa84f,
NoteSeverity.Minor => 0x45818e,
NoteSeverity.Medium => 0xf1c232,
NoteSeverity.High => 0xff0000,
_ => 0xff0000,
};

var payload = new WebhookPayload
{

Username = _serverName,
/*
AvatarUrl = string.IsNullOrWhiteSpace(_avatarUrl) ? null : _avatarUrl,
*/
Embeds = new List<Embed>
{
new()
{
Description = message,
Color = color,
Footer = new EmbedFooter
{
Text = $"{e.AdminUsername}",
/*
IconUrl = string.IsNullOrWhiteSpace(_footerIconUrl) ? null : _footerIconUrl
*/
},
},
},
};

SendDiscordMessage(payload);
}

public void OnJobBan(JobBanEvent e)
{
if (String.IsNullOrEmpty(_webhookUrl))
return;

var expires = e.Expires == null ? Loc.GetString("discord-permanent") : Loc.GetString("discord-expires-at", ("date", e.Expires));
var message = Loc.GetString("discord-jobban-msg",
("username", e.Username),
("role", e.Job.LocalizedName),
("expires", expires),
("reason", e.Reason));


var color = e.Severity switch
{
NoteSeverity.None => 0x6aa84f,
NoteSeverity.Minor => 0x45818e,
NoteSeverity.Medium => 0xf1c232,
NoteSeverity.High => 0xff0000,
_ => 0xff0000,
};

var payload = new WebhookPayload
{
Username = _serverName,
/*
AvatarUrl = string.IsNullOrWhiteSpace(_avatarUrl) ? null : _avatarUrl,
*/
Embeds = new List<Embed>
{
new()
{
Description = message,
Color = color,
Footer = new EmbedFooter
{
Text = $"{e.AdminUsername}",
/*
IconUrl = string.IsNullOrWhiteSpace(_footerIconUrl) ? null : _footerIconUrl
*/
},
},
},
};

SendDiscordMessage(payload);
}

public void OnDepartmentBan(DepartmentBanEvent e)
{
if (String.IsNullOrEmpty(_webhookUrl))
return;
/*
var payload = new WebhookPayload();
var departamentLocName = Loc.GetString(string.Concat("department-", e.Department.ID));
var expires = e.Expires == null ? Loc.GetString("discord-permanent") : Loc.GetString("discord-expires-at", ("date", e.Expires));
var text = Loc.GetString("discord-departmentban-msg",
("username", e.Username),
("department", departamentLocName),
("expires", expires),
("reason", e.Reason));
payload.Content = text;
SendDiscordMessage(payload);
*/
}

private struct WebhookPayload
{
[JsonPropertyName("username")]
public string Username { get; set; } = "";

[JsonPropertyName("avatar_url")]
public string? AvatarUrl { get; set; } = "";

[JsonPropertyName("embeds")]
public List<Embed>? Embeds { get; set; } = null;

[JsonPropertyName("allowed_mentions")]
public Dictionary<string, string[]> AllowedMentions { get; set; } =
new()
{
{ "parse", Array.Empty<string>() },
};

public WebhookPayload()
{
}
}

// https://discord.com/developers/docs/resources/channel#embed-object-embed-structure
private struct Embed
{
[JsonPropertyName("description")]
public string Description { get; set; } = "";

[JsonPropertyName("color")]
public int Color { get; set; } = 0;

[JsonPropertyName("footer")]
public EmbedFooter? Footer { get; set; } = null;

public Embed()
{
}
}

// https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure
private struct EmbedFooter
{
[JsonPropertyName("text")]
public string Text { get; set; } = "";

[JsonPropertyName("icon_url")]
public string? IconUrl { get; set; }

public EmbedFooter()
{
}
}

// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-structure
private struct WebhookData
{
[JsonPropertyName("guild_id")]
public string? GuildId { get; set; } = null;

[JsonPropertyName("channel_id")]
public string? ChannelId { get; set; } = null;

public WebhookData()
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Content.Shared.Database;

namespace Content.Shared.GameTicking;

public sealed class BanEvent : EntityEventArgs
{
public string Username { get; }
public DateTimeOffset? Expires { get; }
public string Reason { get; }
public NoteSeverity Severity { get; }
public string AdminUsername { get; }

public BanEvent(string username, DateTimeOffset? expires, string reason, NoteSeverity severity, string adminusername)
{
Username = username;
Expires = expires;
Reason = reason;
Severity = severity;
AdminUsername = adminusername;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Content.Shared.Database;
using Content.Shared.Roles;

namespace Content.Shared.GameTicking;

public sealed class DepartmentBanEvent : EntityEventArgs
{
public string Username { get; }
public DepartmentPrototype Department { get; }
public DateTimeOffset? Expires { get; }
public string Reason { get; }
public NoteSeverity Severity { get; }
public string AdminUsername { get; }


public DepartmentBanEvent(string username, DateTimeOffset? expires, DepartmentPrototype department, string reason, NoteSeverity severity, string adminusername)
{
Username = username;
Department = department;
Expires = expires;
Reason = reason;
Severity = severity;
AdminUsername = adminusername;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Content.Shared.Database;
using Content.Shared.Roles;

namespace Content.Shared.GameTicking;

public sealed class JobBanEvent : EntityEventArgs
{
public string Username { get; }
public JobPrototype Job { get; }
public DateTimeOffset? Expires { get; }
public string Reason { get; }
public NoteSeverity Severity { get; }
public string AdminUsername { get; }

public JobBanEvent(string username, DateTimeOffset? expires, JobPrototype job, string reason, NoteSeverity severity, string adminusername)
{
Username = username;
Job = job;
Expires = expires;
Reason = reason;
Severity = severity;
AdminUsername = adminusername;
}
}
Loading

0 comments on commit 8f40339

Please sign in to comment.