← All examples
CommunicationEventsSwitch

Run one incident assistant across Slack, Telegram, and Discord

Receive messages from three providers, normalize them once, classify severity, reply through the originating channel, and broadcast critical incidents to every responder workflow.

WOML communication triggers share a provider-independent payload. The business logic does not need three copies merely because users talk through three different products.

The workflow at a glance

Text
Slack mention ─┐
Telegram DM ───┼→ Normalize → Classify → Reply through origin → Critical? → incident.reported
Discord DM ────┘

Add the missing Slack messaging capability

Slack is a built-in trigger and notification provider, but ordinary scripted Slack messaging is intentionally not a public built-in service. Add the narrow operation this project needs in slack-messages.ts:

TypeScript
export async function send(
  conversationId: string,
  text: string,
  botToken: string,
) {
  const response = await services.http.request({
    method: "POST",
    url: "https://slack.com/api/chat.postMessage",
    headers: { authorization: `Bearer ${botToken}` },
    json: { channel: conversationId, text },
    timeout: "15s",
  }, { name: "send-slack-incident-reply" });

  if (!response.data.ok) {
    throw new Error(`Slack rejected the message: ${response.data.error}`);
  }

  return { provider: "slack", messageId: response.data.ts };
}

The workflow

WOML
<woml>
  <imports>
    <module name="slackMessages" from="./slack-messages.ts" />
  </imports>

  <workflow
    id="omnichannel-incident-assistant"
    name="Omnichannel incident assistant"
    description="Classify and route incident reports from three chat providers."
    version="1.0.0"
  >
    <config concurrency="24" rate-limit="240/1m" timeout="2m" queue="incidents" />

    <triggers>
      <slack
        id="slackIncident"
        events="app-mention,direct-message"
        channels="incidents"
        bot-token="{{secrets.SLACK_BOT_TOKEN}}"
        app-token="{{secrets.SLACK_APP_TOKEN}}"
      />
      <telegram id="telegramIncident" events="message" bot-token="{{secrets.TELEGRAM_BOT_TOKEN}}" />
      <discord id="discordIncident" events="app-mention,direct-message" bot-token="{{secrets.DISCORD_BOT_TOKEN}}" />
    </triggers>

    <steps>
      <step id="incident" name="Normalize incident report">
        <script>
          const text = context.payload.text.trim();
          const normalized = text.toLowerCase();
          const criticalTerms = ["outage", "data loss", "security", "payments down"];
          const critical = criticalTerms.some((term) => normalized.includes(term));

          return {
            provider: context.payload.provider,
            conversationId: context.payload.conversationId,
            messageId: context.payload.messageId,
            senderId: context.payload.senderId,
            text,
            critical,
            acknowledgement: critical
              ? "Critical incident received. The response team is being notified now."
              : "Incident received. It has been added to the operations queue."
          };
        </script>
      </step>

      <switch id="reply" name="Reply through originating provider" value="{{context.steps.incident.provider}}">
        <case value="telegram">
          <step id="replyTelegram">
            <script>
              return services.telegram.send({
                botToken: secrets.TELEGRAM_BOT_TOKEN,
                conversationId: context.steps.incident.conversationId,
                text: context.steps.incident.acknowledgement,
                replyToMessageId: context.steps.incident.messageId
              }, { name: "reply-to-telegram-incident" });
            </script>
          </step>
          <result value="{{context.steps.replyTelegram}}" />
        </case>

        <case value="discord">
          <step id="replyDiscord">
            <script>
              return services.discord.send({
                botToken: secrets.DISCORD_BOT_TOKEN,
                conversationId: context.steps.incident.conversationId,
                text: context.steps.incident.acknowledgement,
                replyToMessageId: context.steps.incident.messageId
              }, { name: "reply-to-discord-incident" });
            </script>
          </step>
          <result value="{{context.steps.replyDiscord}}" />
        </case>

        <case value="slack">
          <step id="replySlack">
            <script>
              return services.slackMessages.send(
                context.steps.incident.conversationId,
                context.steps.incident.acknowledgement,
                secrets.SLACK_BOT_TOKEN
              );
            </script>
          </step>
          <result value="{{context.steps.replySlack}}" />
        </case>

        <default>
          <step id="unsupportedProvider"><script>return { provider: "unknown", sent: false };</script></step>
          <result value="{{context.steps.unsupportedProvider}}" />
        </default>
      </switch>

      <choose id="escalation" name="Escalate critical incident">
        <when test="{{context.steps.incident.critical}}">
          <step id="publishCriticalIncident">
            <script>
              const publication = await services.events.emit(
                "incident.reported",
                {
                  severity: "critical",
                  reporter: context.steps.incident.senderId,
                  text: context.steps.incident.text,
                  source: context.steps.incident.provider
                },
                { name: "publish-critical-incident" }
              );
              return { escalated: true, publicationId: publication.publicationId };
            </script>
          </step>
          <result value="{{context.steps.publishCriticalIncident}}" />
        </when>
        <otherwise>
          <step id="queueNormalIncident"><script>return { escalated: false };</script></step>
          <result value="{{context.steps.queueNormalIncident}}" />
        </otherwise>
      </choose>
    </steps>
  </workflow>
</woml>

Configure and run it

Terminal
woml secrets set SLACK_BOT_TOKEN
woml secrets set SLACK_APP_TOKEN
woml secrets set TELEGRAM_BOT_TOKEN
woml secrets set DISCORD_BOT_TOKEN
woml check omnichannel-incident-assistant.woml
woml run omnichannel-incident-assistant.woml

Enable the provider events and permissions described in the Slack, Telegram, and Discord setup guides.

Why this shows WOML's range

One workflow owns the business behavior while built-ins and a ten-line local module handle transport differences. New responder workflows can subscribe to incident.reported without editing the assistant, creating a small event-driven incident platform rather than another provider-specific bot.