
Cómo construir un bot de GitHub con PhantomJS, React y Serverless framework
Este tutorial trata sobre la creación de un bot sin servidor simple que devuelve un gráfico con los principales contribuyentes del repositorio de GitHub durante un período seleccionado. Es relevante para aquellos que tienen alguna experiencia con React, JavaScript, TypeScript, Node.js, Amazon Web Services (AWS) y el marco Serverless.
Puedes consultar el código en Github.
Servicios y herramientas que usaremos
Antes de pasar a la codificación, hagamos una descripción general rápida de los servicios y herramientas de AWS que utilizaremos.
Para recuperar los principales contribuyentes del repositorio, usaremos la API de estadísticas de GitHub, el increíble Nivo para mostrar datos, Storybook para verificar cómo se ve y se siente nuestro gráfico, PhantomJS para convertir HTML en una imagen y un marco sin servidor para interactuar con AWS.
Empecemos
Usaré TypeScript. Si prefiere ES6, deberá configurar Babel.
Primero, debes crear tsconfig.json
en la raíz de tu repositorio. Las opciones a las que prestar atención incluyen:
"module": "commonjs","target": "es5","lib": ["es6", "esnext.asynciterable"],"moduleResolution": "node","jsx": "react"
Luego, crearemos una API simple para consultar estadísticas desde GitHub. Puede seguir la estructura de archivos del repositorio de GitHub o usar la suya propia. Por ejemplo:
Para acceder a la API de GitHub, deberá crear un token de acceso personal.
Este módulo simplemente envía la solicitud con el token proporcionado y recupera los datos.
Visualización de gráficos
Para mostrar los datos, usaremos Nivo y Storybook. Un componente simple puede verse así:
Primero, configure Storybooks ejecutando el siguiente comando en la carpeta raíz:
npm i -g @storybook/cligetstorybook
Copie la carpeta .storybook en el repositorio raíz y reemplace todos los archivos existentes. Contiene la configuración de Webpack y Storybook. Cree una stories
carpeta y coloque una historia de muestra para su componente:
Ejecute npm run storybook
y abra localhost en el navegador. Debería ver el siguiente resultado:

Intenta jugar con las opciones y los datos de prueba. Storybook cambiará el aspecto de inmediato.
Convertir HTML en PNG
Por lo general, los sistemas de chat como Facebook Messenger y Slack no permiten a los usuarios insertar tarjetas HTML en el cuadro de diálogo, por lo que el siguiente paso será crear un asistente que transforme HTML en una imagen PNG.
Usando un script simple con la biblioteca jsdom, podemos imitar el comportamiento del navegador y serializar HTML, así:
createDomForChart
devuelve una nueva instancia de jsdom, y la función de gráfico simplemente llama dom.serialize()
cuando termina la representación del componente.
Con PhantomJS, podemos convertir el marcado en una imagen usando este sencillo script:
Pasamos screenshot.js
a la phantomjs
ruta ejecutable, junto con una cadena HTML, ancho y alto, y recuperamos el búfer con la imagen renderizada.
Puede notar que he estado usando dos binarios PhantomJS (para OS X y Linux). Necesitaremos la versión de Linux más en un entorno de AWS. Puede descargarlos de PhantomJS.org o usar archivos del repositorio.
Atar todo
Ahora, creemos lambda para manejar solicitudes. Recomiendo poner la lógica de renderizado PNG en un servicio separado. Debido a que el binario PhantomJS tiene un tamaño aproximado de 50 MB, ralentiza la implementación si cambia algo en la API. Además, puede reutilizar esta lambda para otros fines.
Comenzaremos creando webpack.config.ts
(para empaquetar el código fuente) y serverless.base.js
(para definir la configuración sin servidor base) en la carpeta raíz.
Si desea saber más sobre casos de uso de configuraciones de JavaScript sin servidor, puede leerlo en mi artículo anterior.
Tendrá que cambiar la implementación y los nombres de los depósitos de imágenes, así:
deploymentBucket: { name: 'com.github-stats....deploys'},environment: { BUCKET: 'com.github-stats....images', GITHUB_TOKEN: '${env:GITHUB_TOKEN}', SLACK_TOKEN: '${env:SLACK_TOKEN}, STAGE: '${self:provider.stage}'},
Esto se debe a que el nombre del depósito debe ser único a nivel mundial.
Convertir HTML en servicio PNG
En primer lugar, crearemos un controlador que devolverá una URL de la imagen generada. El controlador debe validar y procesar el cuerpo de la solicitud:
… Y si todo está bien, debería generar la imagen y ponerla en un bucket de S3.
Creemos webpack.config.ts
para agrupar archivos de origen. Usaremos copy-webpack-plugin
y webpack-permissions-plugin
para incluir binarios de PhantomJS en un paquete, y otorgaremos permisos para su ejecución. Esto requerirá que ejecutemos el comando deploy con sudo ya que Webpack no tiene permisos para modificar los derechos del sistema de archivos de forma predeterminada.
El último paso será usar el serverless.js
archivo para vincular nuestro controlador con un evento de API Gateway.
Ahora, debemos realizar los mismos pasos para el controlador de estadísticas, pero no tenemos que realizar ningún cambio en webpack.config.ts.
La única diferencia es un permiso adicional para invocar lambda:
iamRoleStatements: [ ...baseConfig.provider.iamRoleStatements,{ Effect: 'Allow', Action: ['lambda:InvokeFunction'], Resource: ['*']}]
Configuración del bot de Slack
The last step will be to create a service that will handle message events for the bot. To keep it simple, we’ll handle only mention events. Let’s set up the basic event handler.
We have to handle a verification event from Slack and respond with 200 status and challenge parameters:
callback(null, { body: JSON.stringify({ challenge: (slackEvent as VerificationEvent).challenge }), statusCode: 200});
To properly handle a Slack event, the endpoint has to reply within 3000 milliseconds (3 seconds), so we’ll have to immediately respond and asynchronously send a follow-up message using postMessage API.
In the code above, we parsed the message text to extract a repository name and called out an image stats lambda to retrieve an image URL and send a message back to Slack. You can find the full code of the handler here.
Code for serverless.js and Webpack configurations would be similar to the stats service, so if you have problems with setting it up, take a look at the full source code.
Creating a Slack app
Now let’s create a new Slack app. Go to the Slack API, create a new account (if you have not already done so), create a new app, and add the bot scope in the scopes section.
Go to the “OAuth & Permissions” section in the sidebar.

Add the bot user scope.

Then, you’ll be able to install the app to your organization and get access to tokens.

Deploying the services
You’ll have to install a serverless framework version greater than 1.26 because earlier versions do not support JavaScript configuration files. And I recommend installing slx to simplify the deployment of multiple services.
npm install -g serverlessnpm install -g serviceless
Copy the GitHub and Slack bot tokens, and set them to GITHUB_TOKEN and SLACK_TOKEN environment variables accordingly. Run the following command in the terminal:
sudo GITHUB_TOKEN= SLACK_TOKEN= slx deploy all
As mentioned above, we need sudo to set execute permissions to PhantomJS binaries.
Be patient! Deployment may take a while. At the end you should see a similar output:
Deployment completed successfuly
[app/html-to-png] [completed]:Service Informationservice: html-to-pngstage: devregion: us-east-1stack: html-to-png-devapi keys: Noneendpoints: Nonefunctions: renderToPng: html-to-png-dev-renderToPngServerless: Removing old service versions...[app/slack] [completed]:Service Informationservice: git-stats-slackstage: devregion: us-east-1stack: git-stats-slack-devapi keys: Noneendpoints: POST - //xxxxxxx.execute-api.us-east-1.amazonaws.com/dev/stats/slack/event-handlerfunctions: eventHandler: git-stats-slack-dev-eventHandlerServerless: Removing old service versions...[app/stats] [completed]:Service Informationservice: git-statsstage: devregion: us-east-1stack: git-stats-devapi keys: Noneendpoints: GET - //xxxxxx.execute-api.us-east-1.amazonaws.com/dev/stats/contributors/{owner}/{repo}functions: getContributorStatsImage: git-stats-dev-getContributorStatsImageServerless: Removing old service versions...
The last step will be to subscribe our endpoint to bot mention events.
Select the “Event Subscription” section in the Slack API navigation.

Then paste the event handler URL that you can find in the deploy command output.

Time to play around a bit! Here are some examples of rendered images:
serverless/serverless

facebook/react

plouc/nivo

That’s it!
I hope you found this article helpful. I’d love to see in the comments other types of stats you would like to see in the service.
Please clap if you enjoyed the article! And if you’d like to chat or connect, you can find me on Twitter, GitHub and Linkedin.