National Talk Like an AI Pirate Day

National Talk Like a Pirate Day is on Thursday, and I thought it would be a fun prank to intercept everyone’s messages at work and re-write them in pirate-speak. Unfortunately, I realized this wouldn’t be practical, but I wanted to play with Val Town + ChatGPT so I ended up building something just for fun. Maybe you have a Slack workspace which is lax enough that you can get away with it!

A demo screenshot

Create a Val

Val Town is hands-down the easiest way to deploy a server-side function. You basically just write your code and click the play button. In this case, we have a function that listens for new Slack messages, and it POSTs an in-thread reply via the Slack web API.

You can fork my version on Val Town here.

import { OpenAI } from "https://esm.town/v/std/openai";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=41";

const openai = new OpenAI();
const ok = new Response(undefined, { status: 200 });
const unauthorized = new Response(undefined, { status: 401 });

async function ahoy(prompt: string): Promise<string> {
  const completion = await openai.chat.completions.create({
    messages: [
      {
        role: "system",
        content:
          "You are a pirate. Rephrase everything in the voice of a pirate.",
      },
      { role: "user", content: prompt },
    ],
    model: "gpt-4",
  });

  return completion.choices[0].message.content;
}

export const slackReplyToMessage = async (req: Request) => {
  const { event, token, challenge } = await req.json();

  if (token !== Deno.env.get("slackVerificationToken")) {
    return unauthorized;
  }

  if (challenge) {
    return Response.json({ challenge });
  }

  const skip: boolean[] = [
    event?.type !== "message",
    event?.subtype,
    event?.bot_id,
    event?.bot_profile,
  ];

  if (skip.some((x) => x)) {
    return ok;
  }

  const pirateSpeak = await ahoy(event.text);

  const result = await fetchJSON("https://slack.com/api/chat.postMessage", {
    headers: {
      Authorization: `Bearer ${Deno.env.get("slackToken")}`,
    },
    method: "POST",
    body: JSON.stringify({
      channel: event.channel,
      thread_ts: event.ts,
      text: pirateSpeak,
    }),
  });

  return ok;
};

Create a Slack App

  1. Find and copy the HTTP endpoint for your Val. It should look something like this: https://dctalbot-piratetalk.web.val.run.
  2. Go to https://api.slack.com/apps.
  3. “Create New App” → “From a manifest” → Pick a workspace.
  4. Paste in this YAML:
display_information:
  name: pirate-talk
  description: This bot responds to messages in a channel in the voice of a Pirate.
  background_color: "#000000"
features:
  bot_user:
    display_name: pirate-talk
    always_online: false
oauth_config:
  scopes:
    bot:
      - chat:write
      - channels:history
settings:
  event_subscriptions:
    request_url: YOUR_VAL_ENDPOINT
    bot_events:
      - message.channels
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false
  1. Replace YOUR_VAL_ENDPOINT with the endpoint you copied earlier.
  2. Install the app to your workspace.
  3. In Features → OAuth & Permissions, copy the Bot User OAuth Token.
  4. In Settings → Basic Information, copy the Verification Token.

Configure Val Town secrets

  1. Go to the Val Town environment variables.
  2. Set slackToken to be the Bot User OAuth Token.
  3. Set slackVerificationToken to be the Verification Token.

Try it out

  1. Invite the bot to a channel.
  2. Send a message!

Why it didn’t pan out

It turns out that my original vision of seamlessly rewriting everyone’s messages is not something supported by Slack. Something to do with “trust and security” blah blah blah. Additionally, I don’t think my company would be cool with me giving Val Town and OpenAI access to every new message sent in whatever channels this bot was added to. OTOH this is one way to go out with a bang.