Skip to content

Instantly share code, notes, and snippets.

@cameri
Last active August 10, 2018 04:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cameri/8d04222eb3f6417668ee85fb4cfb70db to your computer and use it in GitHub Desktop.
Save cameri/8d04222eb3f6417668ee85fb4cfb70db to your computer and use it in GitHub Desktop.
const moment = require('moment')
/**
* Draws table fields
*/
const FIELDS = [
'draws.draw_date',
'draws.lowest_crs',
'draws.invitations_issued',
'draws.tie_break',
'draws.date_modified',
'draws.hash'
]
/**
* Gets the latest known draw from database
* @param {any} db Database instance
*/
async function getLatestRecordedDraw (db) {
const [ result ] = await db.select(FIELDS)
.from('express_entry_draws as draws')
.orderBy('draws.date_modified', 'DESC')
.limit(1)
const recordedDraw = {
...result,
draw_date: moment.utc(result.draw_date),
date_modified: moment.utc(result.date_modified),
tie_break: moment.utc(result.tie_break)
}
return recordedDraw
}
/**
* Persists a draw to database
* @param {any} db Database instance
* @param {any} draw Draw record
*/
function save (db, draw) {
return db('express_entry_draws').insert(draw)
}
const API = {
save,
getLatestRecordedDraw
}
module.exports = API
'use strict'
const axios = require('axios')
const Promise = require('bluebird')
const {logger} = require('./util')
const readFileAsync = Promise.promisify(require('fs').readFile)
const config = require('../config/config')
const DEFAULT_TEMPLATE_PATH = './templates/telegram-message-text.html'
const DEFAULT_HEADERS = {
headers: {
'Cache-Control': 'no-cache'
}
}
/**
* Gets the template from a gist or from a local file
*/
async function getGist () {
logger.debug('getGist()')
try {
const response = await axios.get(config.telegram_message_text_url, { ...DEFAULT_HEADERS })
return response.data
} catch (err) {
if (err.status === 404) {
return getTemplateFromDisk()
}
logger.error('getGist() unexpected error: %s. err.stack: %s', err, err.stack)
throw err
}
}
/**
* Gets template from disk
*/
async function getTemplateFromDisk () {
logger.debug('getTemplateFromDisk()')
return readFileAsync(DEFAULT_TEMPLATE_PATH, 'utf8')
}
const API = {
getGist
}
module.exports = API
'use strict'
const { compile } = require('handlebars')
const db = require('./db/knex')
const { logger, emoji } = require('./util')
const { DrawsDao, DrawChatsDao } = require('./db/dao')
const { getCurrentDraw } = require('./express-entry-rounds-service')
const { sendTelegramMessage } = require('./telegram-service')
const { getGist } = require('./gist-service')
/**
* Date format to be used when displaying the draw date
* @see {@link https://momentjs.com/docs/#/displaying/format/|Format}
*/
const DRAW_DATE_FORMAT = 'MMMM DD YYYY'
/**
* Inflates a message template with emojis and information about the draw
* @param {string} template Message template
* @param {any} drawData Current draw information
* @returns {string} Message text
*/
function prepareMessageText (template, drawData) {
logger.debug('prepareMessageText() drawData: %s', JSON.stringify(drawData))
const date = drawData.draw_date.format(DRAW_DATE_FORMAT)
const context = { ...drawData, date, emoji }
return compile(template)(context)
}
/**
* Gets list of chats from DB
* @param {string} template
* @param {*} data
*/
async function sendUpdates (template, data) {
logger.debug('sendUpdates() data: %s', JSON.stringify(data))
// Prepare text message to be sent
const text = await prepareMessageText(template, data)
// Get array of chats from DB
const chats = await DrawChatsDao.getAll(db.getInstance())
// Sends message to each chat sequentially
return chats.forEach(async ({ chat_id: chatId }) => {
try {
await sendTelegramMessage(chatId, text)
} catch (err) {
logger.error('sendUpdates() Unable to send message to chat with Id %s. Reason: %s', chatId, err)
}
})
}
/**
* Gets latest draw from CIC, previously saved draw, compares
* both and sends an update to chat rooms if there's a new draw
*/
async function run () {
try {
// Get latest recorded draw from database and current draw from CIC in parallel
const [ previous, current ] = await Promise.all([
DrawsDao.getLatestRecordedDraw(db.getInstance()),
getCurrentDraw()
])
logger.debug('run() previous draw: %s. current draw: %s', JSON.stringify(previous), JSON.stringify(current))
if (previous.draw_date.isBefore(current.draw_date)) {
// Save new draw
await DrawsDao.save(db.getInstance(), current)
// Get message template from gist
const template = await getGist()
// Send updates to chats
await sendUpdates(template, current)
}
} finally {
// Release db connection(s)
db.release()
}
}
const API = {
run
}
module.exports = API
'use strict'
const axios = require('axios')
const config = require('../config/config')
const { logger } = require('./util')
const TELEGRAM_API_BASE_URL = 'https://api.telegram.org/bot'
/**
* Sends a text message to a Telegram chat
* @param {number} chatId Telegram Chat ID
* @param {string} text Message text
*/
function sendTelegramMessage (chatId, text) {
logger.debug('sendTelegramMessage() chatId:', chatId, 'text:', text)
return axios.get(getSendTelegramMessageUrl(config.telegram_api_key), {
params: {
text,
chat_id: chatId,
parse_mode: 'HTML'
}
})
}
/**
* Gets the URL for Telegram's sendMessage endpoint
* @param {string} apiKey Telegram Bot Api Key
* @returns {string} Telegram's sendMessage endpoint URL
*/
function getSendTelegramMessageUrl (apiKey) {
return `${TELEGRAM_API_BASE_URL}${apiKey}/sendMessage`
}
const API = {
sendTelegramMessage
}
module.exports = API
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment