Created
September 21, 2018 20:28
-
-
Save tuomasj/b2d18d175746f80f873b18398c02b560 to your computer and use it in GitHub Desktop.
Fetch NHL game scores and post the results on a Telegram channel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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