Open runarorama opened 11 months ago
@runarorama can you make a branch for this since by the time someone looks at this, /main
might be in a different state?
The error is:
offset=180:
unexpected =
50 | messageText = Text.toLowercase (events.types.Message.text message)
The definition that can't be parsed is:
events.api.message.channels.doc : Doc
events.api.message.channels.doc =
{{
Handle ''message'' events sent to channels.
# Example
This bot reacts to any message sent to a channel if the message contains
the word ''coffee'', by adding a coffee emoji reaction:
{{ docExample 0 do
coffeeBot =
do
message = !channels
messageText = Text.toLowercase (events.types.Message.text message)
if lib.base.Text.contains "coffee" messageText then
react message "coffee"
else SlackEvents.skip
}}
}}
Note that this definition is pretty-printed without the @typecheck
fence and instead has a docExample
call in a quasiquote, so this is ultimately running into #4384
This is now on a branch: https://share.unison-lang.org/@runarorama/slack/code/@runarorama/updatebug
I did clone @runarorama/slack/@runarorama/updatebug
and then loaded the scratch file in the original description https://gist.github.com/aryairani/2b4589b3de90523e9043e823bdb4864f, but got:
Loading changes detected in ~/work/unison/trunk/scratch.u.
I found and typechecked the definitions in ~/work/unison/trunk/scratch.u. This file has been
previously added to the codebase.
Did something maybe change on the @runarorama/updatebug
branch?
I also did update
, but it's expected to be a no-op, and seemed to be, apart from losing metadata, which I think update
always does now, as we are no longer actively supporting it.
Clone this from Share: https://share.unison-lang.org/@runarorama/slack/code/@runarorama/updatebug
Put this in the scratch file:
}}
SlackWeb.doc : Doc SlackWeb.doc = {{ The ability to make requests to the Slack Web API.
A number of functions are provided to make requests to the Slack Web API:
events.api.message.doc : Doc events.api.message.doc = {{ Handle ''message'' events.
Example
}}
runBot : Text -> Text -> '{cloud.Config, Exception, cloud.State, Http, Services, Remote, cloud.Log, Scratch, SlackWeb, SlackEvents} () -> HttpRequest ->{cloud.Config, Exception, cloud.State, Http, Services, Remote, cloud.Log, Scratch} HttpResponse runBot slackTokenConfigKey slackSigningSecretConfigKey bot = Route.run '(botRoute slackTokenConfigKey slackSigningSecretConfigKey bot)
SlackEvents.or : '{g1, SlackEvents} a -> '{g2, SlackEvents} a -> '{g1, g2, SlackEvents} a SlackEvents.or a b = do h = cases { currentEvent -> k } -> e = currentEvent handle k e with h { SlackEvents.skip -> _ } -> !b { a } -> a handle !a with h
events.api.eventOfType : Text ->{SlackEvents} EventEnvelope events.api.eventOfType typ = match currentEvent with EventCallbackReq token teamId apiAppId env@(EventEnvelope t timestamp user ts payload) cbtype ctx eventId eventTime | t Text.== typ -> env _ -> SlackEvents.skip
tests.demos.coffeeBot : '{Exception, cloud.Log, SlackWeb, SlackEvents} () tests.demos.coffeeBot = do message = !api.message messageText = Text.toLowercase (events.types.Message.text message) if lib.base.Text.contains "coffee" messageText then react message "coffee" else SlackEvents.skip
events.api.reactionAdded.doc : Doc events.api.reactionAdded.doc = {{ Handle ''reaction_added'' events.
Example
}}
events.api.message.channelsNamed.doc : Doc events.api.message.channelsNamed.doc = {{ Handle ''message'' events sent to a specific set of channels.
Takes the names of the channels to handle. Note that these are the names of the channels, not the channel IDs.
Your bot must be a member of the channels you want to handle.
Example
}}
runBot.doc : Doc runBot.doc = {{ Runs a bot in the cloud.
This function is intended to be used with {deployHttp}.
Arguments
Example
}}
events.api.messageOfType.doc : Doc events.api.messageOfType.doc = {{ Handle messages of a particular type.
Example
SlackEvents.or.doc : Doc SlackEvents.or.doc = use SlackEvents skip use Text toLowercase use events.types.Message text use lib.base.Text contains {{ Combines two bots into a single bot that tries the first one and fails over to the second bot if the first one skips the event.
Example
}}
events.api.appMention.doc : Doc events.api.appMention.doc = {{ Handle ''app_mention'' events. These are events that occur when the bot is mentioned in a message.
Example
}}
events.api.appMention : '{SlackEvents} events.types.Message events.api.appMention = do messageOfType "app_mention"
SlackEvents.oneOf : ['{g, SlackEvents} a] ->{SlackEvents} '{g, SlackEvents} a SlackEvents.oneOf ks = List.foldLeft SlackEvents.or SlackEvents.skip ks
(SlackEvents.<|>) : '{g1, SlackEvents} a -> '{g2, SlackEvents} a -> '{g1, g2, SlackEvents} a (SlackEvents.<|>) = SlackEvents.or
events.types.Message.doc : Doc events.types.Message.doc = {{ A message sent to a channel. This is the type received by the bot when Slack sends a ''message'' event. See the Slack API documentation for more information.
Fields
See also
}}
events.api.message.im : '{SlackEvents} events.types.Message events.api.message.im = do msg = messageOfType "message" match channelType msg with Some "im" -> msg _ -> SlackEvents.skip
web.api.reactions.react.doc : Doc web.api.reactions.react.doc = {{ Adds a reaction to a message, given the message and the reaction name. The type of message must be {type events.types.Message}, which is the type of message received by a bot from the {type SlackEvents} ability.
Example
}}
events.api.message.mpim.doc : Doc events.api.message.mpim.doc = {{ Handle ''message'' events sent in multi-person direct messages.
Example
}}
README : Doc README = use Cloud main use SlackEvents oneOf skip use events.types.Message text use lib.base.Text contains use postMessage toChannel toThread use reactions add {{
Slack API
}}
events.api.message : '{SlackEvents} events.types.Message events.api.message = do messageOfType "message"
events.api.message.im.doc : Doc events.api.message.im.doc = {{ Handle ''message'' events sent in direct messages.
Example
web.api.chat.reply.doc : Doc web.api.chat.reply.doc = {{ Posts a reply to a message, given the message and the reply text. The type of message must be {type events.types.Message}, which is the type of message received by a bot from the {type SlackEvents} ability.
Example
}}
botRoute.doc : Doc botRoute.doc = {{ Sets up a {type Route} that handles Slack events and requests to the Slack Web API.
This route is intended to be used with {deployHttp} after {Route.run}. It handles Slack events and requests to the Slack Web API and forwards them to your bot. It also handles the initial handshake request that Slack sends when the bot is first added to a workspace.
Arguments
Example
}}
events.api.message.channels : '{SlackEvents} events.types.Message events.api.message.channels = do msg = messageOfType "message" match channelType msg with Some "channel" -> msg _ -> SlackEvents.skip
SlackEvents.oneOf.doc : Doc SlackEvents.oneOf.doc = use SlackEvents skip use Text toLowercase use events.types.Message text use lib.base.Text contains {{ Combines a list of bots into a single bot that runs the first bot that doesn't skip.
Example
}}
events.types.ReactionEvent.doc : Doc events.types.ReactionEvent.doc = {{ An event that occurs when a reaction is added to a message, file, or file comment.
A reaction event is either a {MessageReaction}, a {FileReaction}, or a {FileCommentReaction}.
Fields
See also
}}
events.api.message.groups : '{SlackEvents} events.types.Message events.api.message.groups = do msg = messageOfType "message" match channelType msg with Some "group" -> msg _ -> SlackEvents.skip
events.api.message.channelsNamed : [Text] ->{Exception, SlackWeb, SlackEvents} events.types.Message events.api.message.channelsNamed names = msg = !channels channelId = Message.channel msg channel = conversations.info channelId if elem (Conversation.name channel) names then msg else SlackEvents.skip
events.api.message.mpim : '{SlackEvents} events.types.Message events.api.message.mpim = do msg = messageOfType "message" match channelType msg with Some "mpim" -> msg _ -> SlackEvents.skip
events.api.eventOfType.doc : Doc events.api.eventOfType.doc = {{ Handle events of a particular type.
Example
events.api.messageOfType : Text ->{SlackEvents} events.types.Message events.api.messageOfType typ = (EventEnvelope t timestamp user ts payload) = eventOfType typ match parsed Message.decoder payload with Right message -> message Left _ -> SlackEvents.skip
SlackEvents.run : EventCallbackReq -> '{g, SlackEvents} a ->{g} Optional a SlackEvents.run event bot = h = cases { currentEvent -> k } -> handle k event with h { SlackEvents.skip -> _ } -> Optional.None { a } -> Some a handle !bot with h
tests.demos.coffeeBot.deploy : '{IO, Exception} () tests.demos.coffeeBot.deploy = Cloud.main do env = Environment.create "coffeeBot" token = getEnv "SLACK_API_TOKEN" secret = getEnv "SLACK_SIGNING_SECRET" setValue env "slack.token" token setValue env "slack.secretKey" secret hash = deployHttp env (runBot "slack.token" "slack.secretKey" coffeeBot) name = ServiceName.create "coffeebot" ServiceName.assign name hash
events.api.message.groups.doc : Doc events.api.message.groups.doc = {{ Handle ''message'' events sent to groups.
Example
}}
botRoute : Text -> Text -> '{g, SlackWeb, SlackEvents} () ->{g, Route, cloud.Config, Exception, Http, Remote, cloud.Log} () botRoute slackTokenConfigKey slackSigningSecretConfigKey bot = use Route <|> sesh = makeSlackSession slackTokenConfigKey webHandled = do SlackWeb.run sesh bot evHandled = do eventRoute webHandled handshaker = internal.handshake <|> evHandled authAdded = authRoute slackSigningSecretConfigKey handshaker !authAdded
SlackEvents.doc : Doc SlackEvents.doc = use Log info {{ The ability to handle Slack events.
The basic usage is to use the {eventOfType} function to handle events of a particular type. For example, to handle all ''message'' events, you would write:
{eventOfType} returns a {type EventEnvelope} value, which contains the original JSON of the event in the {EventEnvelope.payload} field. You can then use the {type Decoder} ability to decode the JSON into a more useful type. See below for a number of functions that do this for you.
The {eventOfType} function will skip any events that are not of the given type. If you want to handle all events, you can use the {currentEvent} ability constructor:
You can then do further pattern matching on the to handle different types of events, and you can use {SlackEvents.skip} to skip events that you don't want to handle.
The {currentEvent} constructor returns a {type EventCallbackReq} value, which contains a number of fields that are common to all events. The {type EventEnvelope} value is contained in the {events.types.EventCallbackReq.event} field.
Event handlers for specific event types
}}
events.api.reactionAdded : '{Exception, SlackEvents} ReactionEvent events.api.reactionAdded = do event = eventOfType "reaction_added" decoder = do use Decoder text use Text ++ use object at! reaction = at! "reaction" text user = EventEnvelope.user event itemUser = UserId (at! "item_user" text) eventTs = eventTimestamp event at! "item" do itemType = at! "type" text match itemType with "message" -> channel = ChannelId (at! "channel" text) ts = ThreadTs (at! "ts" text) MessageReaction reaction user itemUser eventTs channel ts "file" -> file = FileId (at! "file" text) FileReaction reaction user itemUser eventTs file "file_comment" -> file = FileId (at! "file" text) comment = FileCommentId (at! "filecomment" text) FileCommentReaction reaction user itemUser eventTs file comment -> Decoder.fail ("unknown item type: " ++ itemType) Decoder.reraise (parsed decoder (EventEnvelope.payload event))
SlackEvents.run.doc : Doc SlackEvents.run.doc = {{ Runs a bot on a particular event.
Example
}}
unique ability SlackEvents where skip : {SlackEvents} a currentEvent : {SlackEvents} EventCallbackReq
events.internal.eventRoute : '{g, SlackEvents} () ->{g, Route, Exception, cloud.Log} () events.internal.eventRoute k = use Log error use Optional None use badRequest text Route.noCapture POST top requestBody = !bodyUtf8 contentType = List.head (request.header "Content-Type") |> Optional.getOrElse "" event = if Text.startsWith "application/json" contentType then Log.info "Got an event" [("body", requestBody)] Some (logException '(Decoder.run EventCallbackReq.decoder requestBody)) else error "Got an unknown content type" [("Content-Type", contentType)] text "Unsupported content type" None match event with Some ev -> h = cases { SlackEvents.currentEvent -> k } -> handle k ev with h { SlackEvents.skip -> _ } -> () { a } -> a handle !k with h None -> error "Couldn't parse event" [("body", requestBody)] text "Couldn't parse event"