Skip to content

Instantly share code, notes, and snippets.

@tuomasj
Created September 21, 2018 20:28
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 tuomasj/b2d18d175746f80f873b18398c02b560 to your computer and use it in GitHub Desktop.
Save tuomasj/b2d18d175746f80f873b18398c02b560 to your computer and use it in GitHub Desktop.
Fetch NHL game scores and post the results on a Telegram channel
require 'date'
require "open-uri"
require 'json'
# This is just one big file, because it's easy to copy&paste (a.k.a. deploy)
# this script on a cheap VPN and set up a cronjob to run this once per day.
#
#
# Example output with Telegram markdown formatting
# shows the winning team and players who have goals or assists
#
# NHL SCORES 20.09.2018
# ==============
# *MTL* - WSH 5 - 2
# *NYI* - NJD 2 - 0
# *DET* - CHI 4 - 2
# MIN - *DAL* 1 - 3
# *EDM* - WPG 7 - 3
# ANA - *SJS* 3 - 7
# *VAN* - LAK 4 - 3
# LAK - *VGK* 2 - 7
# MTL C Kotkaniemi J 0+0 0 0:00 0 0 0
# *MTL LW Lehkonen A 1+0 1 0:00 1 0 0*
# CHI D Tuulola J 0+0 0 17:07 1 2 0
# *MIN C Koivu M 0+1 1 22:40 4 0 0*
# DAL D Honka J 0+0 0 20:12 1 0 0
# *DAL D Heiskanen M 1+0 1 23:23 1 0 +1*
# DAL LW Hintz R 0+0 0 13:37 1 3 -1
# *EDM RW Puljujarvi J 1+0 1 16:51 4 2 0*
# *WPG D Niku S 0+1 1 16:36 0 0 -1*
# WPG LW Vesalainen K 0+0 0 14:35 0 1 -1
# *SJS C Suomela A 2+1 3 14:16 4 0 +2*
# *SJS RW Donskoi J 1+2 3 16:58 4 0 +3*
# VAN C Granlund M 0+0 0 20:31 3 0 -1
#
#
module Telegram
TELEGRAM_API_TOKEN = "<<telegram API token>>"
def self.telegram_api_url(token, command)
"https://api.telegram.org/bot#{token}/#{command}"
end
def self.send_message(message)
headers = {
"Content-Type" => "application/json"
}
params = {
"chat_id" => "<< telegram channel id>>",
"parse_mode" => "markdown",
"text" => message,
"disable_notification" => true
}
uri = URI.parse(telegram_api_url(TELEGRAM_API_TOKEN, "sendMessage"))
https = Net::HTTP.new(uri.host,uri.port)
https.use_ssl = true
req = Net::HTTP::Post.new(uri.path, headers)
req.body = params.to_json
res = https.request(req)
end
end
module NHL
module Utils
def self.format_date_to_api(timestamp)
timestamp.strftime("%Y-%m-%d")
end
end
module API
def self.fetch_games_by_date(timestamp)
JSON.parse(fetch_by_schedule_from_api(timestamp))
end
def self.fetch_boxscore_by_id(id)
url = "#{NHL_GAMES_BASE_URL}/api/v1/game/#{id}/boxscore"
begin
JSON.parse(URI.parse(url).read)
rescue JSON::ParserError => err
puts "Error JSON parser"
exit
rescue OpenURI::HTTPError => err
puts "Error: fetch_boxscore_by_id!"
puts " url: #{url}"
puts " #{err.message}"
exit
end
end
private
NHL_GAMES_BASE_URL="https://statsapi.web.nhl.com"
def self.fetch_by_schedule_from_api(timestamp)
url = "#{NHL_GAMES_BASE_URL}/api/v1/schedule?expand=schedule.teams&date=#{NHL::Utils.format_date_to_api(timestamp)}"
begin
URI.parse(url).read
rescue OpenURI::HTTPError => err
puts "Error: fetch_by_schedule_from_api!"
puts " url: #{url}"
puts " #{err.message}"
exit
end
end
end
module Parser
def self.parse_games(input, timestamp)
return [] if input.fetch("totalGames", 0).to_i == 0
input.fetch("dates").select do |dates|
dates.fetch("date", nil) == NHL::Utils.format_date_to_api(timestamp)
end.map {|dates| dates.fetch("games", [])}.flatten
end
def self.parse_into_games_hash(input)
Hash[input.map do |game|
[
game.fetch("gamePk", nil),
{
home: {
team: game.fetch("teams", {}).fetch("home", {}).fetch("team", {}).fetch("abbreviation", "xxx"),
score: game.fetch("teams", {}).fetch("home", {}).fetch("score", 0).to_i
},
away: {
team: game.fetch("teams", {}).fetch("away", {}).fetch("team", {}).fetch("abbreviation", "xxx"),
score: game.fetch("teams", {}).fetch("away", {}).fetch("score", 0).to_i
}
}
]
end]
end
def self.parse_players_from_boxscore(input, team, nationality)
input.fetch("teams", {}).fetch(team, {}).fetch("players", {}).map do |player_id, player|
{
id: player_id,
first_name: player.fetch("person", {}).fetch("firstName", "zzz"),
last_name: player.fetch("person", {}).fetch("lastName", "zzz"),
nationality: player.fetch("person", {}).fetch("nationality", "xxx"),
time_on_ice: player.fetch("stats", {}).fetch("skaterStats", {}).fetch("timeOnIce", "0:00"),
assists: player.fetch("stats", {}).fetch("skaterStats", {}).fetch("assists", "0"),
goals: player.fetch("stats", {}).fetch("skaterStats", {}).fetch("goals", "0"),
shots: player.fetch("stats", {}).fetch("skaterStats", {}).fetch("shots", "0"),
hits: player.fetch("stats", {}).fetch("skaterStats", {}).fetch("hits", "0"),
plus_minus: player.fetch("stats", {}).fetch("skaterStats", {}).fetch("plusMinus", "0"),
primary_position: player.fetch("person", {}).fetch("primaryPosition", {}).fetch("abbreviation", "-"),
skaterStats: player.fetch("stats", {}).include?("skaterStats")
}
end.select {|x| x.fetch(:skaterStats, false)}.select {|x| x.fetch(:nationality) == nationality }
end
end
module ScoreOutput
def self.generate_output(final_results, timestamp)
[
"NHL SCORES #{timestamp.strftime("%d.%m.%Y")}",
"==============",
final_results.map do |game|
score = game.fetch(:score, {})
self.headline(score)
end,
"",
final_results.map do |game|
home_team_abbr = game.fetch(:score, {}).fetch(:home).fetch(:team, "xxx")
away_team_abbr = game.fetch(:score, {}).fetch(:away).fetch(:team, "xxx")
[self.player_scores(game.fetch(:home_players), home_team_abbr),
self.player_scores(game.fetch(:away_players), away_team_abbr)]
end.flatten
].join("\n")
end
private
def self.player_scores(players, team_abbr)
players.map do |player|
self.format_bold_if_true(player[:goals]+player[:assists] > 0,
[
team_abbr,
player[:primary_position],
player[:last_name],
player[:first_name][0],
"#{player[:goals]}+#{player[:assists]}",
player[:goals] + player[:assists],
player[:time_on_ice],
player[:shots],
player[:hits],
self.force_sign(player[:plus_minus]),
].join(" ")
)
end
end
def self.force_sign(val)
if val.to_i > 0
"+#{val}"
elsif val.to_i < 0
"#{val}"
else
"0"
end
end
def self.format_bold_if_true(bool, input)
if bool
self.format_bold(input)
else
input
end
end
def self.headline(score)
home = score.fetch(:home, {})
away = score.fetch(:away, {})
home_team_abbr = home.fetch(:team, "xxx")
away_team_abbr = away.fetch(:team, "xxx")
home_score = home.fetch(:score, 0).to_i
away_score = away.fetch(:score, 0).to_i
if home_score > away_score
"#{self.format_bold(home_team_abbr)} - #{away_team_abbr} #{home_score} - #{away_score}"
elsif away_score > home_score
"#{home_team_abbr} - #{self.format_bold(away_team_abbr)} #{home_score} - #{away_score}"
else
"#{home_team_abbr} - #{away_team_abbr} #{home_score} - #{away_score}"
end
end
def self.format_bold(str)
"*#{str}*"
end
end
end
#=================================================================
timestamp = Date.today - 1
games_as_hash = NHL::Parser.parse_into_games_hash( NHL::Parser.parse_games( NHL::API.fetch_games_by_date(timestamp), timestamp))
final_results = games_as_hash.map do |id, score|
ret = NHL::API.fetch_boxscore_by_id(id)
home_players = NHL::Parser.parse_players_from_boxscore(ret, "home", "FIN")
away_players = NHL::Parser.parse_players_from_boxscore(ret, "away", "FIN")
{
score: score,
home_players: home_players,
away_players: away_players
}
end
output_buffer = NHL::ScoreOutput.generate_output(final_results, timestamp)
Telegram.send_message(output_buffer)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment