add the pokemonsync flow.md

For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.

Add the pokemonSync flow

You have clients that talk to both APIs, so put them together to start sending educational Pokémon messages via Slack.

A Pandium integration can be run in normal mode or init mode. The pokemonSync flow is for normal mode.

The goal in this flow is to send a Slack message about a new Pokémon each day. To do this we will need to:

  1. Add the file for this new flow:

Your file structure should now look like this:

     ├── build 
     ├── node_modules 
     ├── src
     │  ├── processLogic
     │  │  └── pokemonSync.ts
     │  ├── index.ts
     │  └── lib.ts
     ├── .env
     ├── package.json
     ├── PANDIUM.yaml
     └── tsconfig.json
  1. Within src/processLogic/pokemonSync.ts add the shell of an asynchronous pokemonSync function.
   export const pokemonSync = async () => {
       console.error('------------------------POKEMON SYNC------------------------')
   }
  1. Within src/index.ts import pokemonSync and invoke it within the run function when the run mode is normal.

The src/index.ts should now look something like this:

   import * as dotenv from 'dotenv'
   dotenv.config()
   import { WebClient } from '@slack/web-api'
   import Pokedex from 'pokedex-promise-v2'
   import { Config, Secret, Context } from './lib.js'
   import { pokemonSync } from './processLogic/pokemonSync.js'

const run = async () => {
       const context = new Context()
       const secrets = new Secret()
       const config = new Config()

console.error(`This run is in mode: ${context['run_mode']}`)
       console.error('------------------------CONFIG------------------------')
       console.error(config)

console.error('------------------------CONTEXT------------------------')
       console.error(context)

const pokeClient = new Pokedex()
       const slackClient = new WebClient(secrets.slack_token)
       
       if (context.run_mode === 'normal') {
           await pokemonSync()
       } 
   }

run().then(
       () => {},
       () => {
           process.exitCode = 1
       }
   )
  1. Run npm run build && npm run start, and you should see the following logged:
   > pokemon-of-the-day@1.0.0 start
   > node build/src/

This run is in mode: normal
   ------------------------CONFIG------------------------
   Config {}
   ------------------------CONTEXT------------------------
   Context { run_mode: 'normal' }
   ------------------------POKEMON SYNC------------------------
  1. Fetch a Pokémon by doing the following:
    • In src/index.ts pass the Pokémon Client to pokemonSync.
    • Add pokeClient as an argument to pokemonSync.
    • Import Pokedex to src/processLogic/pokemonSync.ts to define the type of the pokeClient.
    • Declare a variable nextPokemonId. For now just set it to 247.
    • Pass nextPokemonId to the pokeClient.getPokemonByNamemethod to fetch a Pokémon by ID, because the docs for the Pokémon library state "Any function with the designation 'ByName' can also be passed an integer ID."
    • Log out the results of that fetch.

The pokemonSync.ts file should look something like this:

   import Pokedex from 'pokedex-promise-v2'

export const pokemonSync = async (
       pokeClient: Pokedex
   ) => {
       console.error('------------------------POKEMON SYNC------------------------')
       const nextPokemonId = 247
       const pokemonOfTheDay = await pokeClient.getPokemonByName(nextPokemonId)
       console.error(pokemonOfTheDay)
   }
  1. Run npm run build && npm run start.

You should see the same information logged as before - except that now a large Pokémon object has also been printed. This confirms the Pokémon client within pokemonSync is working, so you can remove the console.error(pokemonOfTheDay).

  1. Add a function to transform the pokemonOfTheDay into a Slack message.
    • Within src add the file transformations.ts.
    • Within transformations.ts define and export the function pokemonToSlackMessage.
    • Review these Slack Web API docs for the postMessage endpoint and the Pokemon Typescript interface to fill out the pokemonToSlackMessage transformation function.

Here is one way transformations.ts could look:

   import { Pokemon } from 'pokedex-promise-v2'
   import { ChatPostMessageArguments } from '@slack/web-api'

export const pokemonToSlackMessage = (
       pokemon: Pokemon,
       channel: string
   ): ChatPostMessageArguments => {
       const abilities = pokemon.abilities
           .map((ability) => ability.ability.name)
           .join(', ')
       const text = `The Pokemon of the Day is *${pokemon.name}*!\n        *Abilties:* ${abilities}\n        *Base Experience:* ${pokemon.base_experience}\n        *Height:* ${pokemon.height}\n        *Weight:* ${pokemon.weight}`
       const message: ChatPostMessageArguments = {
           channel: channel,
           text: text,
           blocks: [
               {
                   type: 'section',
                   text: {
                       type: 'mrkdwn',
                       text: text,
                   },
               },
           ],
       }

if (pokemon.sprites.back_default) {
           message.blocks?.unshift({
               type: 'image',
               image_url: pokemon.sprites.back_default,
               alt_text: `${pokemon.name} sprite`,
           })
       }
       return message
   }
  1. Within pokemonSync use pokemonToSlackMessage and slackClient.chat.postMessage to send a message to each of the Academy's Pokémon trainers.
    • Add slackClient as an argument of pokemonSync and import WebClient from the Slack library to define its type.
    • Within src/index.ts pass slackClient to pokemonSync.
    • Import pokemonToSlackMessage to pokemonSync.ts.
    • Within pokemonSync declare an array called slackMemberIds. Eventually it will hold the IDs of all the Academy's Pokémon trainers, but for development purposes just put your own Slack ID in there.
    • Pass each element of slackMemberIds to pokemonToSlackMessage to create a slackMessage.
    • Pass each slackMessage to slackClient.chat.postMessage.

The pokemonSync.ts file should look something like this:

   import Pokedex from 'pokedex-promise-v2'
   import { WebClient } from "@slack/web-api"
   import {pokemonToSlackMessage} from '../transformations.js'

export const pokemonSync = async (
       pokeClient: Pokedex,
       slackClient: WebClient
   ) => {
       console.error('------------------------POKEMON SYNC------------------------')
       const nextPokemonId = 247
       const pokemonOfTheDay = await pokeClient.getPokemonByName(nextPokemonId)

const slackMemberIds = ['<YOUR-SLACK-MEMBER-ID>']

for (const slackID of slackMemberIds){
           const slackMessage = pokemonToSlackMessage(
               pokemonOfTheDay,
               slackID
           )
           await slackClient.chat.postMessage(slackMessage)
       }
   }
  1. Run npm run build && npm run start. You should get a Slack message about the Pokémon of the Day!

We're not quite done though. If you try running npm run start again you will get another Slack message about the same Pokémon. One of the Academy's requests is that we won't repeat Pokémon.

To accomplish this goal, we will use context to learn which Pokémon have already been used.

Pandium stores the standard out of each tenant's last successful normal sync, so it can be accessed as an environmental context variable during the next run.

  1. Alter the integration so that it prints a standard out during a normal sync.
    • pokemonSync should return a standard out which should be {last_pokemon_id: nextPokemonId }.
    • In src/index.ts print a stringified version of the standard out returned by pokemonSync.
  2. Run npm run build && npm run start.

You should get another slack message about that same Pokémon. However the logs now include the standard out, which should look something like this {"last_pokemon_id":247}.

  1. Add a standard out to your .env.
   PAN_CTX_LAST_SUCCESSFUL_RUN_STD_OUT= '{"last_pokemon_id":247}'
  1. Use that standard out in pokemonSync to ensure the Pokémon of the day is not repeated.
    • In src/index.ts pass context to pokemonSync.
    • In pokemonSync.ts add the argument context to pokemonSync and import the Typescript interface Context to define the type for that new argument.
    • In pokemonSync add the variable lastPokemonId. Its value should be accessed from context.last_successful_run_std_out.
    • In pokemonSync change nextPokemonId so that it will be the next number after lastPokemonId.

Your pokemonSync.ts should look like this:

   import Pokedex from 'pokedex-promise-v2'
   import { WebClient } from "@slack/web-api"
   import {pokemonToSlackMessage} from '../transformations.js'
   import { Context } from '../lib.js'

export const pokemonSync = async (
       pokeClient: Pokedex,
       slackClient: WebClient,
       context: Context
   ) => {
       console.error('------------------------POKEMON SYNC------------------------')
       
       let lastPokemonId = 0
       if (context.last_successful_run_std_out) {
           const lastStdOut = JSON.parse(context.last_successful_run_std_out)
           lastPokemonId = Number(lastStdOut.last_pokemon_id) || 0
       }
       
       const nextPokemonId = lastPokemonId + 1
       const pokemonOfTheDay = await pokeClient.getPokemonByName(nextPokemonId)

const slackMemberIds = ['<YOUR-SLACK-MEMBER-ID>']

for (const slackID of slackMemberIds){
           const slackMessage = pokemonToSlackMessage(
               pokemonOfTheDay,
               slackID
           )
           await slackClient.chat.postMessage(slackMessage)
       }

return {last_pokemon_id: nextPokemonId}
   }
  1. Run npm run build && npm run start. You should get another Slack message about a new Pokémon!

The logs should look like this:

   > pokemon-of-the-day@1.0.0 start
   > node build/src/

This run is in mode: normal
   ------------------------CONFIG------------------------
   Config {}
   ------------------------CONTEXT------------------------
   Context {
     run_mode: 'normal',
     last_successful_run_std_out: '{"last_pokemon_id":247}'
   }
   ------------------------POKEMON SYNC------------------------
   {"last_pokemon_id":248}

Notice the following about the logs:

Your pokemonSync.ts should loook like the one here. In fact, all your integration files should match all the ones in this repository at this commit.

The final step is to see what this all looks like when it is run on Pandium!