diff --git a/GW2SDK.Tests/Features/Guilds/GuildLog.cs b/GW2SDK.Tests/Features/Guilds/GuildLog.cs
index 9c95b74bb..35b44fc2f 100644
--- a/GW2SDK.Tests/Features/Guilds/GuildLog.cs
+++ b/GW2SDK.Tests/Features/Guilds/GuildLog.cs
@@ -36,6 +36,19 @@ public async Task Can_be_found()
case InfluenceActivity influenceActivity:
Assert.True(influenceActivity.Activity.IsDefined());
break;
+ case GuildMission guildMissionActivity:
+ Assert.True(guildMissionActivity.State.IsDefined());
+ if (guildMissionActivity.State == GuildMissionState.Start)
+ {
+ Assert.NotEmpty(guildMissionActivity.User);
+ }
+ else
+ {
+ Assert.Empty(guildMissionActivity.User);
+ }
+
+ Assert.Equal(0, guildMissionActivity.Influence);
+ break;
}
}
);
diff --git a/GW2SDK/Features/Guilds/Logs/GuildLogEntryJson.cs b/GW2SDK/Features/Guilds/Logs/GuildLogEntryJson.cs
index 2eddc8964..a3902ab55 100644
--- a/GW2SDK/Features/Guilds/Logs/GuildLogEntryJson.cs
+++ b/GW2SDK/Features/Guilds/Logs/GuildLogEntryJson.cs
@@ -21,6 +21,8 @@ public static GuildLogEntry GetGuildLogEntry(this JsonElement json)
return json.GetMemberJoined();
case "kick":
return json.GetMemberKicked();
+ case "mission":
+ return json.GetGuildMission();
case "motd":
return json.GetNewMessageOfTheDay();
case "rank_change":
diff --git a/GW2SDK/Features/Guilds/Logs/GuildMission.cs b/GW2SDK/Features/Guilds/Logs/GuildMission.cs
new file mode 100644
index 000000000..2485ad96c
--- /dev/null
+++ b/GW2SDK/Features/Guilds/Logs/GuildMission.cs
@@ -0,0 +1,17 @@
+namespace GuildWars2.Guilds.Logs;
+
+/// A log entry about a started or ended guild mission.
+[PublicAPI]
+[DataTransferObject]
+public sealed record GuildMission : GuildLogEntry
+{
+ /// The ID of the user who started the mission.
+ /// Can be empty.
+ public required string User { get; init; }
+
+ /// The state to which the mission transitioned.
+ public required Extensible State { get; init; }
+
+ /// The amount of guild influence awarded.
+ public required int Influence { get; init; }
+}
diff --git a/GW2SDK/Features/Guilds/Logs/GuildMissionJson.cs b/GW2SDK/Features/Guilds/Logs/GuildMissionJson.cs
new file mode 100644
index 000000000..f92679494
--- /dev/null
+++ b/GW2SDK/Features/Guilds/Logs/GuildMissionJson.cs
@@ -0,0 +1,60 @@
+using System.Text.Json;
+using GuildWars2.Json;
+
+namespace GuildWars2.Guilds.Logs;
+
+internal static class GuildMissionJson
+{
+ public static GuildMission GetGuildMission(this JsonElement json)
+ {
+ RequiredMember id = "id";
+ RequiredMember time = "time";
+ RequiredMember state = "state";
+ RequiredMember influence = "influence";
+ OptionalMember user = "user";
+
+ foreach (var member in json.EnumerateObject())
+ {
+ if (member.NameEquals("type"))
+ {
+ if (!member.Value.ValueEquals("mission"))
+ {
+ ThrowHelper.ThrowInvalidDiscriminator(member.Value.GetString());
+ }
+ }
+ else if (id.Match(member))
+ {
+ id = member;
+ }
+ else if (time.Match(member))
+ {
+ time = member;
+ }
+ else if (state.Match(member))
+ {
+ state = member;
+ }
+ else if (influence.Match(member))
+ {
+ influence = member;
+ }
+ else if (user.Match(member))
+ {
+ user = member;
+ }
+ else if (JsonOptions.MissingMemberBehavior == MissingMemberBehavior.Error)
+ {
+ ThrowHelper.ThrowUnexpectedMember(member.Name);
+ }
+ }
+
+ return new GuildMission
+ {
+ Id = id.Map(static value => value.GetInt32()),
+ Time = time.Map(static value => value.GetDateTimeOffset()),
+ User = user.Map(static value => value.GetString()) ?? "",
+ State = state.Map(static value => value.GetEnum()),
+ Influence = influence.Map(static value => value.GetInt32())
+ };
+ }
+}
diff --git a/GW2SDK/Features/Guilds/Logs/GuildMissionState.cs b/GW2SDK/Features/Guilds/Logs/GuildMissionState.cs
new file mode 100644
index 000000000..ac1a390fd
--- /dev/null
+++ b/GW2SDK/Features/Guilds/Logs/GuildMissionState.cs
@@ -0,0 +1,15 @@
+namespace GuildWars2.Guilds.Logs;
+
+/// The guild mission state transitions.
+[PublicAPI]
+public enum GuildMissionState
+{
+ /// Logged when the mission starts.
+ Start = 1,
+
+ /// Logged when the mission ends successfully.
+ Success,
+
+ /// Logged when the mission ends unsuccessfully.
+ Fail
+}