Using type providers to post slack messages
All text and code copyright (c) 2016 by Rachel Reese. Used with permission.
Original post dated 2016-12-05 available at http://rachelree.se/using-type-providers-to-post-slack-messages/
By Rachel Reese
Happy F# Advent!
I've been playing around with type providers recently, working on a few articles I have coming out for CODE magazine, and I thought it would be fun combine a couple type providers to post updates to slack from the F# Advent Calendar. I didn't want to harass everyone on the F# Software Foundation slack channel (nor did I have the permissions) so this is more a short-and-sweet, to-be-expanded demo of what could be done. ;-)
So, I'll show you how I was able to display today's blog posts, calculate how far along F# Advent is, and send a DM with that info to my account. All in just a couple lines of code of course, because type providers.
Step 1: Get the current day's blog posts
First, I had to do a bit of set up. I'm using the HtmlProvider
and the JsonProvider
from FSharp.Data
,
so I need to reference and open it. I also will often use a keys file for my demos to keep sensitive information, so I load and open that as well.
#r "../packages/FSharp.Data/lib/net40/FSharp.Data.dll"
#load "keys.fs"
open FSharp.Data
open keys
After this, I set up the HtmlProvider
to link to the 2016 Advent Calendar official page, then find the table labeled "F# Advent Calendar in English 2016,"
and return all the rows on that table.
let calendarUrl =
"https://sergeytihon.wordpress.com/2016/10/23/f-advent-calendar-in-english-2016/"
type AdventCalendar = HtmlProvider<calendarUrl>
let rows = AdventCalendar().Tables."F# Advent Calendar in English 2016".Rows
Finding the posts for today means that I need to filter the Date column, so we need to format a date string to match Sergey's formatting. Then, I refined the text a bit by adding a check in case the post wasn't yet up and folding the array into one string value.
let todaysPosts =
rows
|> Array.filter (fun r -> r.Date = System.DateTime.Now.ToString("MMM dd (ddd)"))
|> Array.map
(fun r �>
if r."Post Title" = "" then
"Not yet available by " + r.Author + "."
else r."Post Title" + " by " + r.Author + ".")
|> Array.fold (fun acc s -> acc + "�� " + s + "\n" ) ""
Once I have today's posts, I can reuse rows
to find all the blog post titles, so that I can make the percent done calculation.
let postTitles =
rows
|> Array.map (fun r -> r."Post Title")
Step 2: Make the calculation
Now I have an array of blog post titles, some of which are empty. The complete ratio value is the non-empty values divided by the total number of posts. So, I partition the array to find the non-empty values:
let complete = Array.partition (fun n -> n <> "") postNames
Then, I find the length of that array and the original postTitles
array, convert both to floats, and divide. To find the percent complete, I multiply by 100.
let percent = (complete |> fst |> Array.length |> float)/(Array.length postTitles |> float) * 100.
Finally, I construct a basic message:
let message =
sprintf "Today�s blog posts are:\n%s \nThe advent calendar is %.2f%% complete!"
todaysPosts percent
Step 3: Integrate with Slack
Integrating with slack was the most fun part of this post. I hadn't really worked with the slack API before,
and it was a pleasure to find out that creating an incoming webhook was basically trivial. I did have to first set up an incoming webhook custom integration
for the team. On the slack web site, browse to Manage -> Custom integrations -> Incoming Webhooks
.
Click "add configuration" and you'll be walked through the process. This step will give you a URL to which you can post your message.
Now, posting the message is just a matter of rigging up the JsonProvider
to send it. I set up an example bit of JSON and use that to create a new WebHook
type.
[<Literal>]
let webhookjson =
"""{"text": "This is a line of text in a channel.\nAnother line."}"""
type WebHook = JsonProvider<webhookjson, RootName="Message">
Next, I set up the call.
let newPost = WebHook.Message(message)
Finally, I make the request, to the URL that was provided when I set up the incoming webhook for the team.
newPost.JsonValue.Request keys.slackWebHookUrl
And voila! I have a private message sent to me:
Slack messages with current F# Advent posts.
Originally, I'd planned to use the SwaggerProvider to call Slack. They have a pretty cool API with a lot of fun options, but since I ended up making a single call, it just doesn't make sense here. Next post! :-)