diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00db369..f05ddba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 2.2.3.0 (2025-09-15)
+* [#156](https://github.com/MercuryTechnologies/slack-web/pull/156)
+ Implement `message_deleted` event.
+
# 2.2.2.0 (2025-09-15)
* [#153](https://github.com/MercuryTechnologies/slack-web/pull/153)
Implement `channel_shared` and `channel_unshared` events.
diff --git a/src/Web/Slack/Experimental/Events/Types.hs b/src/Web/Slack/Experimental/Events/Types.hs
index 47794f8..9cdc6fd 100644
--- a/src/Web/Slack/Experimental/Events/Types.hs
+++ b/src/Web/Slack/Experimental/Events/Types.hs
@@ -244,6 +244,20 @@ data MessageUpdateEvent = MessageUpdateEvent
$(deriveFromJSON snakeCaseOptions ''MessageUpdateEvent)
+-- | A message was deleted from a channel or DM.
+--
+--
+--
+-- @since 2.2.3.0
+data MessageDeletedEvent = MessageDeletedEvent
+ { channel :: ConversationId
+ , ts :: Text
+ , deletedTs :: Text
+ }
+ deriving stock (Show)
+
+$(deriveFromJSON snakeCaseOptions ''MessageDeletedEvent)
+
-- | FIXME: this might be a Channel, but may also be missing some fields/have bonus
-- because Slack is cursed.
data CreatedChannel = CreatedChannel
@@ -286,6 +300,8 @@ $(deriveFromJSON snakeCaseOptions ''ChannelLeftEvent)
-- | A channel has been shared with an external workspace.
--
--
+--
+-- @since 2.2.2.0
data ChannelSharedEvent = ChannelSharedEvent
{ connectedTeamId :: TeamId
, channel :: ConversationId
@@ -298,6 +314,8 @@ $(deriveFromJSON snakeCaseOptions ''ChannelSharedEvent)
-- | A channel has been unshared with an external workspace.
--
--
+--
+-- @since 2.2.2.0
data ChannelUnsharedEvent = ChannelUnsharedEvent
{ previouslyConnectedTeamId :: TeamId
, channel :: ConversationId
@@ -364,6 +382,8 @@ data Event
= EventMessage MessageEvent
| EventBotMessage BotMessageEvent
| EventMessageChanged
+ | -- | @since 2.2.3.0
+ EventMessageDeleted MessageDeletedEvent
| -- | Weird message event of subtype channel_join. Sent "sometimes", according
-- to a random Slack blog post from 2017:
--
@@ -372,8 +392,10 @@ data Event
EventChannelJoinMessage
| EventChannelCreated ChannelCreatedEvent
| EventChannelLeft ChannelLeftEvent
- | EventChannelShared ChannelSharedEvent
- | EventChannelUnshared ChannelUnsharedEvent
+ | -- | @since 2.2.2.0
+ EventChannelShared ChannelSharedEvent
+ | -- | @since 2.2.2.0
+ EventChannelUnshared ChannelUnsharedEvent
| -- | @since 2.1.0.0
EventAppHomeOpened AppHomeOpenedEvent
| EventUnknown Value
@@ -387,6 +409,7 @@ instance FromJSON Event where
("message", Nothing) -> EventMessage <$> parseJSON @MessageEvent (Object obj)
("message", Just "bot_message") -> EventBotMessage <$> parseJSON @BotMessageEvent (Object obj)
("message", Just "message_changed") -> pure EventMessageChanged
+ ("message", Just "message_deleted") -> EventMessageDeleted <$> parseJSON @MessageDeletedEvent (Object obj)
("message", Just "channel_join") -> pure EventChannelJoinMessage
-- n.b. these are unified since it is *identical* to a Message event with
-- a bonus files field
diff --git a/tests/Web/Slack/Experimental/Events/TypesSpec.hs b/tests/Web/Slack/Experimental/Events/TypesSpec.hs
index 37e5dc1..262b9d3 100644
--- a/tests/Web/Slack/Experimental/Events/TypesSpec.hs
+++ b/tests/Web/Slack/Experimental/Events/TypesSpec.hs
@@ -12,6 +12,7 @@ spec = describe "Types for Slack events" do
(oneGoldenTestDecode @SlackWebhookEvent)
[ "messageExample"
, "messageChange"
+ , "message_deleted"
, "message_rich_text"
, "message_file_share"
, "message_file_share_slack_connect"
diff --git a/tests/golden/SlackWebhookEvent/message_deleted.golden b/tests/golden/SlackWebhookEvent/message_deleted.golden
new file mode 100644
index 0000000..de54cb8
--- /dev/null
+++ b/tests/golden/SlackWebhookEvent/message_deleted.golden
@@ -0,0 +1,20 @@
+EventEventCallback
+ ( EventCallback
+ { eventId = EventId
+ { unEventId = "Ev043QRVT9FE" }
+ , teamId = TeamId
+ { unTeamId = "T043DB835ML" }
+ , eventTime = MkSystemTime
+ { systemSeconds = 1663969383
+ , systemNanoseconds = 0
+ }
+ , event = EventMessageDeleted
+ ( MessageDeletedEvent
+ { channel = ConversationId
+ { unConversationId = "C043YJGBY49" }
+ , ts = "1663969383.001500"
+ , deletedTs = "1663966382.046509"
+ }
+ )
+ }
+ )
diff --git a/tests/golden/SlackWebhookEvent/message_deleted.json b/tests/golden/SlackWebhookEvent/message_deleted.json
new file mode 100644
index 0000000..a166893
--- /dev/null
+++ b/tests/golden/SlackWebhookEvent/message_deleted.json
@@ -0,0 +1,37 @@
+{
+ "token": "aaaa",
+ "team_id": "T043DB835ML",
+ "api_app_id": "A0442TUPHGR",
+ "event": {
+ "type": "message",
+ "subtype": "message_deleted",
+ "hidden": true,
+ "channel": "C043YJGBY49",
+ "previous_message": {
+ "client_msg_id": "de349a9f-62a4-4293-b64d-701a1f566e8a",
+ "type": "message",
+ "text": "This message will be deleted",
+ "user": "U043H11ES4V",
+ "ts": "1663966382.046509",
+ "team": "T043DB835ML"
+ },
+ "deleted_ts": "1663966382.046509",
+ "ts": "1663969383.001500",
+ "event_ts": "1663969383.001500",
+ "channel_type": "channel"
+ },
+ "type": "event_callback",
+ "event_id": "Ev043QRVT9FE",
+ "event_time": 1663969383,
+ "authorizations": [
+ {
+ "enterprise_id": null,
+ "team_id": "T043DB835ML",
+ "user_id": "U0442US8QGH",
+ "is_bot": true,
+ "is_enterprise_install": false
+ }
+ ],
+ "is_ext_shared_channel": false,
+ "event_context": "aaaa"
+}