Giter Site home page Giter Site logo

mattdonders / nhl-twitter-bot Goto Github PK

View Code? Open in Web Editor NEW
25.0 25.0 12.0 8.57 MB

🚨 Hockey Game Bot is a Python application that sends important NHL events to social media platforms in (near) real time.

License: MIT License

Python 98.91% Shell 1.03% Dockerfile 0.06%
game-bot hockey nhl nhl-api python social-media sports twitter

nhl-twitter-bot's Introduction

πŸ‘‹ Hello there - I am Matt Donders!

I like nerdy things, photography, beer & whiskey. Husband & dad of two.

"It's kind of fun to do the impossible." - Walt Disney
  • 🐦 Β Find me on Twitter
  • 🚨 Β Check out the original Game Bot

nhl-twitter-bot's People

Contributors

dependabot[bot] avatar dmcp89 avatar erinbrown avatar mattdonders avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

nhl-twitter-bot's Issues

Added error handling for requests to non-NHL APIs

Add error handling when using requests.get for non-NHL APIs which may be down or change their JSON.

For example, this will break the bot if there is no data or an invalid date is passed.

FANTASY_LABS_BASE_URL = 'https://www.fantasylabs.com/api/lines/4'
lineup_date = game.game_date_local
lineup_team = team.team_name

logging.info("Checking Fantasy Labs for today's lineup for %s", lineup_team)
fantasy_labs_url = f"{FANTASY_LABS_BASE_URL}/{lineup_team}/{lineup_date}"
r = requests.get(fantasy_labs_url).json()

Either wrap it in a try / except or check the return code and handle errors.

Trouble getting set up

Hello there. I am currently trying to spin up a Minnesota Wild game bot using a Mac, but I'm having some trouble running the hockeygamebot.sh file. I see the echo in the code, but I have no idea what it means (still fairly new to coding). This is what I get in the terminal after running:

./hockeygamebot.sh
Usage: ./hockeygamebot.sh <VENV|PYTHONEXEC> <SCRIPT-PARAMS:optional>
VENV|PYTHONEXEC is the path to the virtual environment to active or the Python binary to use.
SCRIPT-PARAMS are the optional parameters being passed into the game bot script.

I feel like I followed the README pretty much to the T, but it looks like I still need to change a path somewhere. The virtual environment is active in the terminal, but I don't believe that's what it means.

I also only received three keys from Twitter. Not sure if they used to give out four... I received an API key, API key secret, and a bearer token. I'm sort of just guessing that I can use the secret key in the consumer_secret and also the access_secret, but I'm obviously not 100%.

Some guidance on any of this would be awesome, thank you!

Handling of Postponed Games

Due to COVID-19 we need to be able to handle games that have a code of 9 and / or are marked as Postponed.

"status" : {
    "abstractGameState" : "Preview",
    "codedGameState" : "9",
    "detailedState" : "Postponed",
    "statusCode" : "9",
    "startTimeTBD" : false
}

Scrape NST at 0:00

Lately NST has been getting stuck at 0:00 time at the end of the period. Add that as logic to determine when to scrape so charts can be generated quicker.

Chart Enhancements

Add the following charts / chart enhancements.

  • Make a CF/60 & CA/60 chart similar to the xG Rate Per 60 chart.
  • Add league average to the existing Expected Goals / 60 chart.

Clean up the confirmed lines sleep logic

If the lineups for a team are never confirmed or the API doesn't return the correct data set the time.sleep(3600) code might make the bot sleep past the time of a game.

Add some type of error handling or time check here.

Season Series - Too Ling

Sometimes the season series tweets are too long. For each player in their respective stats categories, drop their first name to save space.

Use the last name code snippet - " ".join(full_name.split()[1:]) - to make sure you always get players with space separated last names.

Download & Tweet Video

For Highlights, download & re-upload the video.

This may require the temporary use of the Twython library as Tweepy won't have this until V4 according to their GitHub & Discord.

Last name is truncated for players with space separated last names.

When a player has a last name that is separated by a space, the last name is truncated because we don't extend to the end of the split list.

The fix should be as simple as converting .split()[1] to.split()[1:] for both the preferred and other teams.

logging.debug("Looping through preferred goals for stat box.")
preferred_boxscore_players = boxscore_preferred["players"]
for id, player in preferred_boxscore_players.items():
try:
if player["stats"]["skaterStats"]["goals"] == 1:
player_name = player["person"]["fullName"]
player_first_name = player_name.split()[0]
player_first_letter = player_first_name[0]
player_last_name = player_name.split()[1]
player_abbrev_name = f"{player_first_letter}. {player_last_name}"
pref_goals_array.append(player_abbrev_name)
elif player["stats"]["skaterStats"]["goals"] > 1:
player_goals = player["stats"]["skaterStats"]["goals"]
player_name = player["person"]["fullName"]
player_first_name = player_name.split()[0]
player_first_letter = player_first_name[0]
player_last_name = player_name.split()[1]
player_abbrev_name = f"{player_first_letter}. {player_last_name} [{player_goals}]"
pref_goals_array.append(player_abbrev_name)
except KeyError:
logging.debug("Stats for %s not available.", player["person"]["fullName"])
logging.debug("Looping through preferred goals for stat box.")
other_boxscore_players = boxscore_other["players"]
for id, player in other_boxscore_players.items():
try:
if player["stats"]["skaterStats"]["goals"] == 1:
player_name = player["person"]["fullName"]
player_first_name = player_name.split()[0]
player_first_letter = player_first_name[0]
player_last_name = player_name.split()[1]
player_abbrev_name = f"{player_first_letter}. {player_last_name}"
other_goals_array.append(player_abbrev_name)
elif player["stats"]["skaterStats"]["goals"] > 1:
player_goals = player["stats"]["skaterStats"]["goals"]
player_name = player["person"]["fullName"]
player_first_name = player_name.split()[0]
player_first_letter = player_first_name[0]
player_last_name = player_name.split()[1]
player_abbrev_name = f"{player_first_letter}. {player_last_name} [{player_goals}]"
other_goals_array.append(player_abbrev_name)
except KeyError:
logging.debug("Stats for %s not available.", player["person"]["fullName"])

Add Recap & Condensed Game Videos

Recap is located in Content Feed at media.epg[3].items[0].id
Condensed Game is located in Content Feed at media.epg[2].items[0].id (Called "Extended Highlights")

Conditional Imports for Raspberry Pi

Due to installation time and resources for Pandas & Numpy, create conditional imports so that section of code just skipped.

When shotmap functionality is introduced break up the requirements file into these two options -

  • Base requirements & shotmap / data science extras : requirements.txt and requirements-extras.txt
  • Two separate files per platforms : requirements-rpi.txt and requirements-all.txt

Team names with a period causes URL issues

Verify that all URLs resolve for teams with periods in their names.

For example, the current Daily Faceoff URL for the St. Louis Blues is https://www.dailyfaceoff.com/teams/st.-louis-blues/line-combinations/ but the correct url is https://www.dailyfaceoff.com/teams/st-louis-blues/line-combinations/ (removed period).

2019-12-04 09:15:06 - root - INFO - Sending Third Party URL Request - https://www.dailyfaceoff.com/teams/st.-louis-blues/line-combinations/
2019-12-04 09:15:07 - root - ERROR - Exception getting Daily Faceoff goalies - try again next loop.
2019-12-04 09:15:07 - root - ERROR - 'NoneType' object has no attribute 'find'

Career Milestones Shouldn't Use Ordinal

Currently career milestones tweets use the ordinal version of the number and instead should use the regular number.

🚨 100th CAREER GOALS! 🚨

St. Louis Blues GOAL! 🚨🚨

Alex Pietrangelo (7) scores on a wrist shot from 29 feet away with 16:08 left in the 3rd period.

Blues: 2 / Sabres: 2

#STLvsBUF

Penalty Shots are 0-Minute Penalties

When a penalty shot is awarded the bot tweets a 0-minute penalty instead of indicating the event was a penalty shot.

Ryan Johansen takes a 0-minute penalty shot penalty for ps - slash on breakaway and heads to the penalty box with 10:55 remaining in the 3rd period. Predators are headed to the power play!

Predators PK: 75.8% (27th)
Sharks PP: 16.7% (22nd)

#SJSvsNSH

Fix Non-Roster Player URL Attribute

Following the below git diff format, remove the brackets around the non-roster player ID from the hockeygamebot/nhlapi/roster.py file.

--dbddiff --git a/hockeygamebot/nhlapi/roster.py b/hockeygamebot/nhlapi/roster.py
index d38ba01..77f0761 100644
--- a/hockeygamebot/nhlapi/roster.py
+++ b/hockeygamebot/nhlapi/roster.py
@@ -73,7 +73,7 @@ def nonroster_player_attr_by_id(player_id, attribute):
     Returns:
         string: Attribute of the person requested.
     """
-    api_player_url = f"/people/{[player_id]}"
+    api_player_url = f"/people/{player_id}"
     api_player = api.nhl_api(api_player_url).json()
     player_attr = api_player["people"][0][attribute]
     return player_attr

Team matchup names can overflow from box

As seen in the sample image attached, the matchup text (Maple Leafs vs Blackhawks) can overflow from its box. The text should be resized automatically based on the length of the of the text to fit within the bounds of the box.

do804e-xoay-nnh

V2 Docker

Figure out the best way to Dockerize V2 of the bot. Need to determine what can live in the config file and what can / should be environment variables passed into the gamebot.

My first thought is to check for Docker in the load_config() method and if we detect a Docker instance then convert the environment variables into a similar config dictionary.

Migrate All URLs to New File

Currently all URLs are stored in config.yaml and loaded up via utils.load_config() function.

We want to take the URLs out of the configuration file because that is client specific and they have a local copy of it and move the URLs into a separate file.

This new file & retrieval function is implemented, but need to move all URL access patterns to use this URL.

def load_urls():
    """ Loads the URLs yaml file and returns a yaml object / dictionary..

    Args:
        None

    Returns:
        A yaml (dictionary) object.
    """

    with open(URLS_PATH) as ymlfile:
        urls = yaml.load(ymlfile, Loader=yaml.FullLoader)

    return urls

Support Team-Specific Time Zones

Currently the game bot uses the local time zone of the machine it is running on instead of the local time zone of the team the bot is running for.

Use the timeZone key within the teams API endpoint dictionary to drive this value.

"id" : 1,
"name" : "New Jersey Devils",
"link" : "/api/v1/teams/1",
"venue" : {
  "name" : "Prudential Center",
  "link" : "/api/v1/venues/null",
  "city" : "Newark",
"timeZone" : {
  "id" : "America/New_York",
  "offset" : -5,
  "tz" : "EST"
}

1-Minute Remaining

Check if the period time updates even if there are no events so that we can do a 1-minute remaining in the period tweet.

Sometimes periods are low event and the bot is quiet so this is there just to keep it in the feeds and provide context of the period.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.