Wisemonk: A slackbot to move discussions from Slack to Discourse

Then there was the fact that we had so many channels and direct messages and group chats. It multiplexed my brain and left me in a constant state of anxiety, feeling that I needed to always be on guard. Dave Teare, Curing Our Slack Addiction

The Beginning

— Manish Jain

Sitting in a Japanese restaurant, waiting for my lunch, this is the line that hit me hard. I’d experienced this exact thing in my previous company.

Every block of time dedicated to coding was either interrupted by pings on the chat system, or a consistent feeling that you’re missing out. Away from the desk, phones would start ringing every evening. And they’d ring some more on the weekends, Sunday evenings in particular. We were always doing something — the feeling was like being on unpaid pager duty. People are talking, and if I’m not around to show my presence, it would mean I’m not working. But what was getting done, I wasn’t so clear about.

So, when we started exploring options for both internal and external communication for Dgraph , we wanted to do things differently. We’d focus on asynchronous communication, so people can take their time before jumping in. People can reply when they get to it — this might be hours later. And there’ll be an incentive to modify your reply as more things become clear to you or you find better words to express yourself. Something better than Email, but not so distracting as Slack. The winner came out to be Discourse by Jeff Atwood and his team(edit: not Discord, another chat app).

Having run it for over two months, we couldn’t have been happier. Discourse sits somewhere between emails and real-time chat systems. You feel like you’re having a real-time conversation. But it won’t interrupt you in the middle of a 3-hour block of time you’ve set for distraction-free coding. Discourse has become such an integral part of our company that we abandoned making decisions in meetings entirely. All decisions happen over discourse, so everyone has a chance to voice their opinions and suggestions at their pace. And others can embark on fact-finding and put figures together to support or deny arguments. I’ve seen a lot smarter discussions, and hence decisions happening over our Discourse forum than over any other medium.

Don’t get me wrong. We still need Slack for casual chit chat and team bonding. But, Slack is an addiction. You write one thing, someone replies; then someone else jumps in; and the result is an hour-long frenzy, where everyone feels like they’ve contributed a lot. But a few hours later, you can’t pinpoint what changed.

Most useful discussions require a bit more time and thought. More than when someone else is typing while you’re in the middle of your sentence.

This is what lead to Wisemonk. This bot would push the conversation away from Slack into Discourse. And this has to happen just when the team is getting sucked into the frenzy. Before it’s too late

How we built it

— Pawan Rawal

Wisemonk makes use of the Slack RTM API. We make good use of the concurrency features in Go. For each Slack channel passed in channels flag, we initialise an instance of type Counter. The counter keeps track of the messages and other states for a Slack channel. Each counter has a buffered channel (called messages) to which Slack messages are sent.

cmap := make(map[string]*Counter)
for _, cid := range schannels {
  c := &Counter{channelId: cid}
  c.messages = make(chan *slack.Msg, 500)
  cmap[cid] = c
  go c.checkOrIncr(rtm, wg, memmap)
go listen(rtm)

The listen function which runs as a goroutine gives us access to the messages sent on Slack. We use this library to authenticate with and get messages from Slack. Then the listen function passes messages on the relevant Go channel.

case *slack.MessageEvent:
  if sm, ok := msg.Data.(*slack.MessageEvent); ok {
  // Putting the message on the Counter it belongs to
  m := sm.Msg
  if c, ok := cmap[m.Channel]; ok {
    c.messages <- &m

The checkOrIncr method which runs as a goroutine for every counter keeps a count of the number of messages exchanged in a given interval. Below is the basic version of the method. It runs a never ending for loop. Now every time a message is received on the messages channel of the counter instance, it adds the message. NewTicker function from the time package provides us with a Ticker, which sends the time on a channel with the period specified. So every 10 seconds we get a time value and check if the count of messages exchanged increases the maxmsg that we passed as a flag. If it does, then we send a warning to the relevant Slack channel.

func (c *Counter) checkOrIncr(rtm *slack.RTM, wg sync.WaitGroup, memmap map[string]string) {
   defer wg.Done()
   ticker := time.NewTicker(time.Second * 10)
   for {
       select {
       case msg := <-c.messages:
           c.Increment(msg, memmap)
       case <-ticker.C:
           count := c.Count()
           if count >= *maxmsg {
               go sendMessage(c, rtm)

Once the basic functionality was in place, we added integration with discourse. Wisemonk stores the last n messages exchanged, creates a discourse topic with the messages when they exceed the max count and shares the URL with us on Slack.

You can create a discourse topic on demand too and get the URL for it. This makes shifting conversations to Discourse super easy.

For the weekends or in the evenings when you want to have a casual chat and don’t want to be interrupted by Wisemonk, you could ask him to meditate, and it won’t disturb you for a while.

Wisemonk in Action

Wisemonk alerting us when we talk too much Wisemonk alerting us when we talk too much

Asking wisemonk to create a topic Asking wisemonk to create a topic

Asking wisemonk to meditate Asking wisemonk to meditate

Parting Thoughts

Setting up wisemonk is super easy and we have found that it has enhanced our productivity a lot.

It's open source and available at Github, so feel free to contribute to it. We would love to hear about your usage of Wisemonk.

Yoda guides actions. Wisemonk guides conversations.

Get blog post to your inbox

The world’s most advanced, hosted GraphQL backend

Slash GraphQL Private Beta