all repos — archie @ dd1c9af419865b0a2048dbdc29b535d6448f9466

A minimal Hugo Theme

exampleSite/content/posts/tg-gh.md (view raw)

  1---
  2title: Telegram Bot for  GitHub Actions
  3date: "2020-04-01"
  4description: Make a Telegram bot with Node.js and use it with GitHub Actions for sending notifications to you about the repo.
  5tldr: Making GitHub Actions with Js Code
  6---
  7## Telegram
  8[Telegram](https://telegram.org/) is a cloud-based mobile and desktop messaging app with a focus on security and speed. It is free to use and extensively hackable. It also has a good bot support system. The API is also easy to implement and has many wrappers for building bots with the API.
  9
 10## GitHub Actions
 11[GitHub Actions](https://github.com/features/actions) is a CI/CD runtime for your GitHub repository. You can run almost anything from scripts to docker containers. You can build, test and deploy your code with GitHub Actions. All these actions are called workflows and workflows differ in the job they're doing. These maybe test workflows, build ones or deployment ones. You can find all the actions on GitHub in the [marketplace](https://github.com/marketplace?type=actions)
 12
 13## Building the Bot
 14### Prerequisites
 15- Basic JavaScript Knowledge
 16- Basic GitHub Knowledge
 17- Telegram Account
 18  
 19> There are templates for building actions. Here we're gonna start from scratch
 20
 21### Environment Setup
 22- **Node**, You can download node from their [website](https://nodejs.org/en/download/)
 23- NPM comes with node, so you don't have to worry about it.
 24- Initialize the Project
 25```shell
 26$ git init ## initialize a new git repository for version management
 27---
 28$ npm init
 29```
 30- **dotenv**, Dotenv can be downloaded via
 31```shell
 32$ npm i dotenv
 33---
 34$ yarn add dotenv
 35```
 36- **node-telegram-bot-api**, node-telegram-bot-api is a simple wrapper for building telegram bots. You can download it via
 37```shell
 38$ npm i node-telegram-bot-api
 39---
 40$ yarn add node-telegram-bot-api
 41```
 42- **@zeit/ncc**, NCC is a Simple CLI for compiling a Node.js module into a single file, together with all its dependencies, GCC-style. It's a dev dependency and can be downloaded
 43```shell
 44yarn add --dev @zeit/ncc
 45---
 46npm i -D @zeit/ncc
 47```
 48
 49
 50#### Folder Structure
 51The `dist` folder will be automatically created. `action.yml` will be made
 52
 53```
 54.
 55├── dist
 56│   └── index.js
 57├── index.js
 58├── action.yml
 59├── README.md
 60└── package.json
 61
 62```
 63- `index.js` is the file we're defining the bot
 64- `action.yml` is the file we'll define the action and it's behaviours
 65
 66## Making the Bot
 67We need to get an API bot token from telegram. For that Go to Telegram and Search for `Botfather`. It's a bot.
 68![](bfather.png)
 69Create a new bot with the `/newbot` command and get the API key. We'll need that, also talk to `jsondump` bot and get your chat id. The output may be like this, so
 70```json
 71{
 72  "update_id": 143943779,
 73  "message": {
 74    "message_id": 181575,
 75    "from": {
 76      "id": 123456 // this is what we need
 77      "is_bot": false,
 78      "first_name": "Tg Name",
 79      "username": "TG Username",
 80      "language_code": "en"
 81    },
 82    "chat": {
 83      "id": 123456,
 84      "first_name": "Tg Name",
 85      "username": "TG Username",
 86      "type": "private"
 87    },
 88    "date": 1584119424,
 89    "text": "message"
 90  }
 91}
 92```
 93This will be needed for further use and We need to add it to the repo secrets which can be found in the repo settings. Be careful to add it as `token` and `chat` like as shown below
 94![](scr.png)
 95
 96### Writing the Action and Building the Bot
 97Fire up the terminal/cmd and make a new folder. Install the dependencies. Run the following command
 98```shell
 99$ touch index.js action.yml
100```
101Open your favourite text editor within the folder or with the file. We'll define the bot in `index.js`
102
103```javaScript   
104require("dotenv").config
105const Bot = require('node-telegram-bot-api');
106const {
107    INPUT_STATUS: ipstatus,
108    INPUT_TOKEN: tgtoken,//Telegram api token
109    INPUT_CHAT: chatid,// Telegram Chat ID
110    INPUT_IU_TITLE: ititle,// Issue title
111    INPUT_IU_NUM: inum,// Issue Number
112    INPUT_IU_ACTOR: iactor,// Issue made by
113    INPUT_IU_BODY: ibody,// Issue Body
114    INPUT_PR_NUM: pnum,// PR Number
115    INPUT_PR_STATE: prstate,// PR Opened, reponed or closed
116    INPUT_PR_TITLE: ptitle,// PR Title
117    INPUT_PR_BODY: pbody,// Body of the PR
118    GITHUB_EVENT_NAME: ghevent,// Name of the trigger event
119    GITHUB_REPOSITORY: repo,// Repository the trigger was made from
120    GITHUB_ACTOR: ghactor,// User who triggered the action
121    GITHUB_SHA: sha,// Commit ID
122    GITHUB_WORKFLOW: ghwrkflw// Workflow Name
123} = process.env;
124
125const bot = new Bot(tgtoken)
126```
127First, we're defining the dotenv for config and initializing Telegram Bot. Here we're defining the alias variables for the *environment variables*. You might notice an `INPUT_` for almost every environment variable, this is because GitHub Actions pass the env variable with an INPUT prefix. Other env variables are action's default environment variables. Then we initialized the bot with the API token.
128
129GitHub actions could be triggered with Issues, Pull Request or Pushes. You can find the trigger events [here](https://help.github.com/en/actions/reference/events-that-trigger-workflows). Here we're gonna get a message from the bot when an *Issue* or *Pull Request* or a *Push* event has happened.
130
131```js
132const evresp = (gevent) => {
133    switch (gevent) {
134
135        case "issues":
136            return `
137❗️❗️❗️❗️❗️❗️
138        
139Issue ${prstate}
140
141Issue Title and Number  : ${ititle} | #${inum}
142
143Commented or Created By : \`${iactor}\`
144
145Issue Body : *${ibody}*
146
147[Link to Issue](https://github.com/${repo}/issues/${inum})
148[Link to Repo ](https://github.com/${repo}/)
149[Build log here](https://github.com/${repo}/commit/${sha}/checks)`
150        case "pull_request":
151            return `
152🔃🔀🔃🔀🔃🔀
153PR ${prstate} 
154        
155PR Number:      ${pnum}
156        
157PR Title:       ${ptitle}
158        
159PR Body:        *${pbody}*
160        
161PR By:          ${ghactor}
162        
163[Link to Issue](https://github.com/${repo}/pull/${pnum})
164[Link to Repo ](https://github.com/${repo}/)
165[Build log here](https://github.com/${repo}/commit/${sha}/checks)`
166        default:
167            return `
168⬆️⇅⬆️⇅
169            
170ID: ${ghwrkflw}
171        
172Action was a *${ipstatus}!*
173        
174\`Repository:  ${repo}\` 
175        
176On:          *${ghevent}*
177        
178By:            *${ghactor}* 
179        
180Tag:        ${process.env.GITHUB_REF}
181        
182[Link to Repo ](https://github.com/${repo}/)
183            `
184    }
185}
186```
187In these lines of code, we're just initializing a switch statement for the responses. We're also declaring an anonymous function to use the switch responses via a function later. We're using all the defined variables in the switch. You can check the [trigger Events](https://help.github.com/en/actions/reference/events-that-trigger-workflows) to get how the event is triggered and what keyword should be used.
188
189Now for the last part of the Js file, we just take the response from the switch and assign it to a constant. Then we use the `sendMessage` function of the `node-telegram-bot-api` to send the message to the bot with the chatid and the output as the arguments.
190```js
191const output = evresp(ghevent)
192```
193bot.sendMessage(chatid,output,{parse_mode : "Markdown"})
194## Compiling and Minifying the Js code 
195Since we have installed `@zeit/ncc` and this is used for the making the whole program with all the APIs to a single file and we need to use NCC for that. We just need to run
196```shell 
197yarn run ncc build index.js -C -m -o dist
198``` 
199or you might wanna add the following to you `package.json` file, and run `npm run test` to compile and minify the code.
200```json
201"scripts": {
202    "test": "ncc build index.js -C -m -o dist"
203  },
204```
205This will create a `dist` folder with and `index.js` file which contains the compiled code.
206
207## Making it a valid action
208For making this Js file a valid action, we need to add an `action.yml` file. The action.yml for this action is like this
209```yml
210name: 'Action Name'
211description: 'Action Descreption'
212author: '<author name>'
213inputs: 
214  chat:
215    description: 'Chat to send: chat id or @channel_name'
216    required: true
217  token:
218    description: 'Telegram Bot token'
219    required: true
220  status:
221    description: 'Job status'
222    required: true
223  iu_title: 
224    description: 'Issue Title'
225    default: ${{ github.event.issue.title }}
226  iu_num:
227    description: 'Issue Number'
228    default: ${{ github.event.issue.number }}
229  iu_actor: 
230    description: 'Issue Triggerer'
231    default: ${{ github.event.issue.user.login }}
232  iu_com:
233    description: 'Issue Comment'
234    default: ${{github.event.comment.body}}
235  pr_state:
236    description: 'State of the PR'
237    default: ${{ github.event.action }}
238  pr_num:
239    description: 'PR Number'
240    default: ${{ github.event.number }}
241  pr_title:
242    description: 'Title of the PR'
243    default: ${{ github.event.pull_request.title }}
244  pr_body:
245    description: 'Body/Contents of the PR'
246    default: ${{ github.event.pull_request.body }}
247runs:
248  using: "node12"
249  main: "dist/index.js"
250branding:
251  icon: 'repeat'  
252  color: 'green'
253```
254Here we're defining the Input variables to be loaded for the action in GitHub's runtime environemt. All these `default` data are taken from the response of the webhooks which are send by GitHub when a trigger event is occured. You can find out more in the [Action Documentation Here](https://help.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context).
255
256```yml
257runs:
258  using: "node12"
259  main: "dist/index.js"
260```
261Here we are defining that this is a node action and should run in an environment with node, and the file which should be run, here the `index.js` file in the `dist` folder. That should do it. Create a new commit and push it to a repo. **Create a new tag** and this action will appear in the [marketplace](https://github.com/marketplace?type=actions).
262
263### Defining a workflow to test your action
264GitHub Action workflows are defined using the `.yml` syntax. Here is an example of a sample workflow for this action
265
266```yml
267name: <Workflow Name>
268
269on:
270  push:
271  pull_request:
272    types: [opened, closed]
273  issues:
274    types: [opened, closed, reopened]
275jobs:
276  build:
277    runs-on: ubuntu-latest
278
279    steps:
280      - name: <AnyName>
281        uses: <username>/<repo>@master
282        if: always()
283        with:
284          chat: ${{ secrets.chat }}
285          token: ${{ secrets.token }}
286          status: ${{ job.status }}
287```
288
289
290The Complete code for the bot is
291```js
292//Initializing dotenv and the bot
293require("dotenv").config
294const Bot = require('node-telegram-bot-api');
295// aliasing the environment variables 
296const {
297    INPUT_STATUS: ipstatus, 
298    INPUT_TOKEN: tgtoken, //Telegram api token
299    INPUT_CHAT: chatid,// Telegram Chat ID
300    INPUT_IU_TITLE: ititle,// Issue title
301    INPUT_IU_NUM: inum,// Issue Number
302    INPUT_IU_ACTOR: iactor, // Issue made by
303    INPUT_IU_BODY: ibody, // Issue Body
304    INPUT_PR_NUM: pnum, // PR Number
305    INPUT_PR_STATE: prstate, // PR Opened, reponed or closed
306    INPUT_PR_TITLE: ptitle, // PR Title
307    INPUT_PR_BODY: pbody, // Body of the PR
308    GITHUB_EVENT_NAME: ghevent, // Name of the trigger event
309    GITHUB_REPOSITORY: repo, // Repository the trigger was made from
310    GITHUB_ACTOR: ghactor, // User who triggered the action
311    GITHUB_SHA: sha, // Commit ID
312    GITHUB_WORKFLOW: ghwrkflw // Workflow Name
313} = process.env;
314
315const bot = new Bot(tgtoken)
316// Function to return the response for the specific trigger
317const evresp = (gevent) => {
318    switch (gevent) {
319//Switch statement for issues
320        case "issues":
321            return `
322❗️❗️❗️❗️❗️❗️
323        
324Issue ${prstate}
325
326Issue Title and Number  : ${ititle} | #${inum}
327
328Commented or Created By : \`${iactor}\`
329
330Issue Body : *${ibody}*
331
332[Link to Issue](https://github.com/${repo}/issues/${inum})
333[Link to Repo ](https://github.com/${repo}/)
334[Build log here](https://github.com/${repo}/commit/${sha}/checks)`
335// Switch statement for Pull Requests
336        case "pull_request":
337            return `
338🔃🔀🔃🔀🔃🔀
339PR ${prstate} 
340        
341PR Number:      ${pnum}
342        
343PR Title:       ${ptitle}
344        
345PR Body:        *${pbody}*
346        
347PR By:          ${ghactor}
348        
349[Link to Issue](https://github.com/${repo}/pull/${pnum})
350[Link to Repo ](https://github.com/${repo}/)
351[Build log here](https://github.com/${repo}/commit/${sha}/checks)`
352        default:
353// switch statement for Pushes
354            return `
355⬆️⇅⬆️⇅
356            
357ID: ${ghwrkflw}
358        
359Action was a *${ipstatus}!*
360        
361\`Repository:  ${repo}\` 
362        
363On:          *${ghevent}*
364        
365By:            *${ghactor}* 
366        
367Tag:        ${process.env.GITHUB_REF}
368        
369[Link to Repo ](https://github.com/${repo}/)
370            `
371    }
372}
373// assigning the output to a variable
374const output = evresp(ghevent)
375// sending the message
376bot.sendMessage(chatid,output,{parse_mode : "Markdown"})
377```
378
379
380------
381
382You can try out many different items using actions and this is just a sample action to get you started. Maybe sending Cat GIFs if the build succeded on the pull request or sending a welcome message to a first time contributor. You imagination is the limit😄 and **Never Stop being ⚡️**