From f25ef4167e16e81b26663f73ee549164ad3940df Mon Sep 17 00:00:00 2001 From: cottongin Date: Wed, 3 Oct 2018 13:10:18 -0500 Subject: [PATCH 1/4] initial commit --- .gitignore | 2 + README.md | 1 + __init__.py | 49 +++++ abbrv.json | 546 ++++++++++++++++++++++++++++++++++++++++++++++ config.py | 33 +++ local/__init__.py | 1 + plugin.py | 453 ++++++++++++++++++++++++++++++++++++++ test.py | 15 ++ 8 files changed, 1100 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100644 abbrv.json create mode 100644 config.py create mode 100644 local/__init__.py create mode 100644 plugin.py create mode 100644 test.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99d3ecc --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.bak +__pycache__/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..971a7bf --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Fetches CFB scores diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..9a39a68 --- /dev/null +++ b/__init__.py @@ -0,0 +1,49 @@ +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +""" +CFBScores: Fetches CFB scores +""" + +import sys +import supybot +from supybot import world + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.authors.unknown + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +# This is a url where the most recent plugin package can be downloaded. +__url__ = '' + +from . import config +from . import plugin +if sys.version_info >= (3, 4): + from importlib import reload +else: + from imp import reload +# In case we're being reloaded. +reload(config) +reload(plugin) +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + from . import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/abbrv.json b/abbrv.json new file mode 100644 index 0000000..ef04217 --- /dev/null +++ b/abbrv.json @@ -0,0 +1,546 @@ +{ + "chattanooga": [ + "chatt" + ], + "connecticut": [ + "uconn", + "conn" + ], + "louisiana monroe": [ + "ulm" + ], + "south alabama": [ + "usa" + ], + "air force": [ + "airforce", + "usafa", + "usaf", + "af" + ], + "akron": [ + "akr" + ], + "alabama": [ + "ala", + "bama", + "gump", + "tide" + ], + "appalachian state": [ + "app st", + "app state" + ], + "arizona": [ + "zona" + ], + "arizona state": [ + "asu", + "azst", + "sparky" + ], + "arkansas": [ + "ark", + "arky", + "bert", + "razorbacks", + "ar" + ], + "arkansas state": [ + "arst", + "arkst", + "ark state" + ], + "auburn": [ + "aub", + "au", + "aubrun", + "war eagle", + "awburn", + "barners" + ], + "baylor": [ + "bay", + "baylol", + "baylour" + ], + "big 12": [ + "big12" + ], + "big ten": [ + "big10", + "b1g" + ], + "boise state": [ + "bsu", + "boise st", + "boisest", + "boise" + ], + "boston college": [ + "bc" + ], + "bowling green": [ + "bg", + "bgsu" + ], + "byu": [ + "mormons", + "lds" + ], + "california": [ + "cal" + ], + "central michigan": [ + "central mich", + "cmu" + ], + "cincinatti": [ + "cincy", + "cinci", + "cinc" + ], + "clemson": [ + "fuck", + "clem" + ], + "colorado": [ + "col", + "cu", + "co", + "buffs", + "buffaloes" + ], + "colorado state": [ + "csu" + ], + "duke": [ + "dook" + ], + "east carolina": [ + "ecu" + ], + "eastern michigan": [ + "emu" + ], + "florida": [ + "fla", + "gators", + "jorts", + "uf" + ], + "florida atlantic": [ + "fau " + ], + "florida intl": [ + "florida international", + "fiu" + ], + "florida state": [ + "flst", + "noles", + "crablegs", + "crab legs", + "seminoles", + "fl st", + "fsu" + ], + "fresno state": [ + "fresno", + "fresno st" + ], + "georgia": [ + "dwags", + "uga", + "dawgs", + "h8", + "hate", + "ugh", + "u(sic)ga" + ], + "georgia southern": [ + "gaso", + "gasouthern", + "gsu" + ], + "georgia state": [ + "state", + "gastate", + "gast" + ], + "georgia tech": [ + "nerds", + "buzz", + "bees", + "gt", + "gtech", + "gatech" + ], + "houston": [ + "hou", + "uh", + "coogs" + ], + "illinois": [ + "uiuc", + "ill" + ], + "iowa": [ + "hawkeyes" + ], + "iowa state": [ + "isu", + "iowa st" + ], + "jacksonville": [ + "jville" + ], + "kansas": [ + "ku", + "jayhawks" + ], + "kansas state": [ + "k-state", + "kstate", + "kst", + "ksu", + "ks", + "k state", + "wizard" + ], + "kentucky": [ + "uk" + ], + "louisiana lafayette": [ + "ull" + ], + "louisiana tech": [ + "latech" + ], + "louisville": [ + "lou", + "ul", + "lville", + "big papa", + "papa john", + "papa johns", + "go big papa" + ], + "lsu": [ + "corndogs", + "louisiana state", + "louisiana state university" + ], + "maryland": [ + "umd", + "md", + "terps", + "turtles" + ], + "massachusetts": [ + "umass", + "massachussets" + ], + "memphis": [ + "mem" + ], + "miami": [ + "canes", + "miami florida", + "miami fl", + "mia", + "hurricanes", + "umiami", + "miami", + "da u" + ], + "miami (oh)": [ + "mu", + "miamioh", + "miami oh", + "miami ohio" + ], + "michigan": [ + "mich", + "mich", + "umich", + "khakis", + "harbaugh", + "um" + ], + "michigan state": [ + "sparty", + "msu", + "michst", + "michigan st" + ], + "middle tennessee": [ + "mtsu", + "middle tennessee state" + ], + "minnesota": [ + "minn", + "minny", + "gophers", + "dillybars", + "dilly bar" + ], + "mississippi state": [ + "miss state", + "land mass state", + "ms st", + "msst", + "missst", + "misst", + "clanga", + "land mass st", + "miss st" + ], + "missouri": [ + "mizzou", + "miz", + "misery", + "mizz" + ], + "nc state": [ + "ncst", + "ncsu", + "wolfpack" + ], + "nebraska": [ + "neb", + "huskers", + "unl", + "corn" + ], + "new mexico": [ + "unm", + "bob davie" + ], + "north carolina": [ + "nc", + "unc" + ], + "north dakota state": [ + "ndsu" + ], + "northern illinois": [ + "niu" + ], + "northwestern": [ + "nu", + "nw" + ], + "notre dame": [ + "nd", + "noter dame", + "irish", + "domers" + ], + "ohio state": [ + "aosu", + "osu", + "tosu" + ], + "oklahoma": [ + "ou", + "ok" + ], + "oklahoma state": [ + "okst" + ], + "old dominion": [ + "odu" + ], + "ole miss": [ + "rebels", + "land mass", + "ole mass", + "ole" + ], + "oregon": [ + "ducks", + "ore", + "uo", + "quack" + ], + "oregon state": [ + "orst" + ], + "osu": [ + "buckeyes" + ], + "penn state": [ + "psu", + "penn st", + "ttsci" + ], + "pittsburgh": [ + "pitt" + ], + "purdue": [ + "purdont", + "boilermakers" + ], + "rutgers": [ + "rut", + "ru", + "buttgers" + ], + "sam houston state": [ + "shsu" + ], + "san diego state": [ + "sdsu", + "san diego st" + ], + "san josé state": [ + "san jose", + "sjsu", + "san jose state", + "san jose st" + ], + "smu": [ + "southern methodist", + "smoo" + ], + "south carolina": [ + "cocks", + "sakerlina", + "gamecocks", + "s car", + "carolina", + "usce", + "scar" + ], + "south florida": [ + "usf", + "bulls" + ], + "southern utah": [ + "suu" + ], + "stanford": [ + "stanny", + "trees", + "stan", + "furd" + ], + "syracuse": [ + "syr", + "cuse" + ], + "tcu": [ + "frogs" + ], + "tennessee": [ + "checkers", + "smokey", + "tenn", + "vols", + "ten" + ], + "tennessee tech": [ + "tenn tech", + "tntech", + "tn tech" + ], + "tennessee-martin": [ + "tenn martin", + "utm" + ], + "texas": [ + "texsa", + "taxes", + "tejas", + "tex", + "longhorns", + "hookem", + "ut", + "zerosa" + ], + "texas a&m": [ + "aggies", + "atm", + "a&m", + "tam", + "gigem", + "inthebag", + "tamu" + ], + "texas san antonio": [ + "utsa" + ], + "texas tech": [ + "tt", + "ttu", + "ttech", + "tortillas", + "sunglasses" + ], + "the citadel": [ + "citadel" + ], + "usc": [ + "uscw", + "southern cal" + ], + "utah": [ + "utes" + ], + "utah state": [ + "usu", + "utah st" + ], + "utica college": [ + "utica" + ], + "vanderbilt": [ + "vandy" + ], + "virginia": [ + "uva", + "va" + ], + "virginia tech": [ + "vatech", + "vt", + "vtech" + ], + "wake forest": [ + "wf", + "wake", + "wakef" + ], + "washington": [ + "uw", + "wash" + ], + "washington state": [ + "wazzu", + "wazzy", + "cougs", + "cuogs", + "wsu" + ], + "west virginia": [ + "wvu", + "wv", + "couches" + ], + "western kentucky": [ + "wku", + "hilltoppers" + ], + "western michigan": [ + "wmu", + "row the boat", + "rowtheboat", + "pj fleck" + ], + "wisconsin": [ + "wiscy", + "wis", + "wisc" + ] +} diff --git a/config.py b/config.py new file mode 100644 index 0000000..459e030 --- /dev/null +++ b/config.py @@ -0,0 +1,33 @@ +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +from supybot import conf, registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('CFBScores') +except: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified themself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('CFBScores', True) + + +CFBScores = conf.registerPlugin('CFBScores') +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(CFBScores, 'someConfigVariableName', +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/local/__init__.py b/local/__init__.py new file mode 100644 index 0000000..e86e97b --- /dev/null +++ b/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..60d6ad7 --- /dev/null +++ b/plugin.py @@ -0,0 +1,453 @@ +# CFBScores +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +import requests +import pendulum +import json +import html + +from supybot import utils, plugins, ircutils, callbacks, schedule +from supybot.commands import * +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('CFBScores') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +class CFBScores(callbacks.Plugin): + """Fetches CFB scores""" + threaded = True + + def __init__(self, irc): + self.__parent = super(CFBScores, self) + self.__parent.__init__(irc) + + self.SCOREBOARD = ('http://site.api.espn.com/apis/site/v2/sports/' + 'football/college-football/scoreboard') + + self.FUZZY_DAYS = ['yesterday', 'tonight', 'today', 'tomorrow', + 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'] + + with open("abbrv.json", 'r') as json_file: + self.abbrv = json.load(json_file) + + def periodicCheckGames(): + #print('Periodic check') + self.CFB_GAMES = self._fetchGames(None, '') + #print(self.CFB_GAMES) + + periodicCheckGames() + + try: # check scores. + schedule.addPeriodicEvent(periodicCheckGames, 20, now=False, name='fetchCFBscores') + except AssertionError: + try: + schedule.removeEvent('fetchCFBscores') + except KeyError: + pass + schedule.addPeriodicEvent(periodicCheckGames, 20, now=False, name='fetchCFBscores') + + @wrap([optional('text')]) + def cfbrankings(self, irc, msg, args, filter_team=None): + """Fetches CFB Rankings""" + + url = 'http://site.api.espn.com/apis/site/v2/sports/football/college-football/rankings' + + try: + data = requests.get(url) + data = data.json() + except: + irc.reply('Error fetching rankings') + return + + rankings = data['rankings'][0] + week = ' ' + data['latestWeek']['displayValue'] + title = data['rankings'][0]['name'] + + # #1 ALA (3-0 | -) + + output = [] + for team in rankings['ranks']: + tmp = '#{0}{3} {1} {2}' + + if '+' in team['trend']: + trend = self._green(team['trend']) + elif '-' in team['trend'] and team['trend'] != '-': + trend = self._red(team['trend']) + else: + trend = team['trend'] + tmp = tmp.format( + team['current'], + self._bold(team['team']['abbreviation']), + team['recordSummary'], + '({})'.format(trend) if trend != '-' else '' + ) + + if filter_team: + if filter_team.upper() == team['team']['abbreviation']: + output.append(tmp) + break + else: + output.append(tmp) + + if filter_team and len(output) == 1: + irc.reply('{}: {}'.format(title + week, output[0])) + elif filter_team and not output: + irc.reply('No results found for {}'.format(filter_team)) + else: + irc.reply('{}: {}'.format(title + week, ' | '.join(team for team in output[:11]))) + irc.reply('{}'.format(' | '.join(team for team in output[11:]))) + + @wrap([getopts({'week': 'positiveInt', 'conf': 'positiveInt'}), optional('text')]) + def cfb(self, irc, msg, args, optlist, team=None): + """[--week #] []""" + + optlist = dict(optlist) + week = optlist.get('week') + conf = optlist.get('conf') + + team = self._parseInput(team) + + if (conf==80 or conf==81 or conf==35) and not team: + irc.reply('ERROR: You must provide a team') + return + + # override games cache + if week or conf or team == 'today': + self.CFB_GAMES = self._fetchGames(team, conf, week) + + if not self.CFB_GAMES: + #print('Pulling fresh') + self.CFB_GAMES = self._fetchGames(team, conf) + if not self.CFB_GAMES: + irc.reply('No games found') + return + + games = self._parseGames(self.CFB_GAMES, team) + games = self._sortGames(games) + + reply_string = self._replyAsString(team, games) + + #print(reply_string) + for string in reply_string: + irc.reply(string) + + # reset games cache to current week + if week: + self.CFB_GAMES = self._fetchGames(team, conf) + elif conf: + self.CFB_GAMES = self._fetchGames(team, conf='') + + def _parseInput(self, team): + if not team: + return None + else: + # tbd + return team + + def _fetchGames(self, team, conf="80", week=None): + team = 'all' if not team else team.upper() + date = pendulum.now('US/Pacific') + conf = '' if not conf else conf + + url = self.SCOREBOARD + '?groups={}'.format(conf) + if team != 'all' and team != 'TODAY' and team != 'INP': + url += '&limit=300' + + if week: + url += '&week={}'.format(week) + + #print(url) + games = requests.get(url) + games = games.json() + + games = games['events'] + + if team != 'all' and team != 'TODAY' and team != 'INP': + ngames = [] + + # check abbreviation first + for game in games: + if team == game['competitions'][0]['competitors'][0]['team']['abbreviation'] \ + or team == game['competitions'][0]['competitors'][1]['team']['abbreviation']: + #or team in html.unescape(game['competitions'][0]['competitors'][0]['team']['location']).upper() \ + #or team in html.unescape(game['competitions'][0]['competitors'][1]['team']['location']).upper(): + ngames.append(game) + if not ngames: + if team == 'HAWAII': + team = "HAWAI'I" + for game in games: + #print(team) + if team == html.unescape(game['competitions'][0]['competitors'][0]['team']['location']).upper() \ + or team == html.unescape(game['competitions'][0]['competitors'][1]['team']['location']).upper(): + ngames.append(game) + return ngames + + + return games + + def _parseGames(self, games, team=None): + new_games = [] + + if team: + if team.lower() != 'all' and team.lower() != 'today' and team.lower() != 'inp': + for idx, game in enumerate(games): + if team.upper() == game['competitions'][0]['competitors'][0]['team']['abbreviation'] \ + or team.upper() == game['competitions'][0]['competitors'][1]['team']['abbreviation']: + #or team in html.unescape(game['competitions'][0]['competitors'][0]['team']['location']).upper() \ + #or team in html.unescape(game['competitions'][0]['competitors'][1]['team']['location']).upper(): + games = [games.pop(idx)] + break + + for game in games: + date = pendulum.parse(game['date']).in_tz('US/Pacific') + today = pendulum.today('US/Pacific') + new_game = {} + new_game['id'] = game['id'] + new_game['time'] = pendulum.parse(game['date']).in_tz('US/Eastern').format('ddd h:mm A zz') + new_game['date'] = pendulum.parse(game['date']).in_tz('US/Eastern').format('dddd, MMMM Do, h:mm A zz') + new_game['home_full'] = game['competitions'][0]['competitors'][0]['team']['location'] + new_game['home'] = game['competitions'][0]['competitors'][0]['team']['abbreviation'] + new_game['home_id'] = game['competitions'][0]['competitors'][0]['team']['id'] + new_game['away_full'] = game['competitions'][0]['competitors'][1]['team']['location'] + new_game['away'] = game['competitions'][0]['competitors'][1]['team']['abbreviation'] + new_game['away_id'] = game['competitions'][0]['competitors'][1]['team']['id'] + new_game['status'] = game['status']['type']['state'] + new_game['shortDetail'] = game['status']['type']['shortDetail'] + new_game['final'] = game['status']['type']['completed'] + new_game['in_progress'] = False + # Rankings + new_game['home_team_rank'] = game['competitions'][0]['competitors'][0]['curatedRank'].get('current') + new_game['away_team_rank'] = game['competitions'][0]['competitors'][1]['curatedRank'].get('current') + + # Odds + try: + new_game['odds'] = '{} (O/U: {:.0f})'.format(game['competitions'][0]['odds'][0]['details'], game['competitions'][0]['odds'][0]['overUnder']) + except Exception as e: + new_game['odds'] = '' + print(e) + + #print(new_game['odds']) + + if new_game['status'] == 'in' and not new_game['final']: + new_game['in_progress'] = True + try: + new_game['last_play'] = game['competitions'][0]['situation']['lastPlay']['text'] + except: + new_game['last_play'] = '' + new_game['pos'] = game['competitions'][0]['situation'].get('possession') + new_game['rz'] = game['competitions'][0]['situation'].get('isRedZone') + new_game['desc'] = game['competitions'][0]['situation'].get('downDistanceText') + new_game['clock'] = game['status']['type']['shortDetail'] + try: + new_game['clock'] = new_game['clock'].split('-')[0].strip() + ' ' + self._green(new_game['clock'].split('-')[1].strip()) + except: + new_game['clock'] = new_game['clock'] + if 'Delayed' in new_game['clock']: + new_game['clock'] = self._orange('DLY') + if 'Halftime' in new_game['clock']: + new_game['clock'] = 'HT' + new_game['HT'] = True + else: + new_game['HT'] = False + + elif new_game['status'] == 'post': + new_game['in_progress'] = False + new_game['broadcasts'] = '{}'.format(', '.join(item['media']['shortName'] for item in game['competitions'][0]['geoBroadcasts'])) + new_game['h_score'] = int(game['competitions'][0]['competitors'][0]['score']) + new_game['a_score'] = int(game['competitions'][0]['competitors'][1]['score']) + if team == 'today': + if date.day == today.day: + new_games.append(new_game) + elif team == 'inp': + if new_game['in_progress']: + new_games.append(new_game) + else: + new_games.append(new_game) + + return new_games + + def _sortGames(self, games): + sorted_games = sorted(games, key=lambda k: k['final']) + + return sorted_games + + def _replyAsString(self, team, games): + reply_strings = [] + tmp_strings = [] + #print(games[0]) + half_point = len(games)//2 + #print(half_point) + + def _parseScores(away, ascr, home, hscr, arnk, hrnk): + print(ascr, arnk, hscr, hrnk) + if ascr > hscr: + astr = '{} {}'.format(self._bold(away), self._bold(ascr)) + hstr = '{} {}'.format(home, hscr) + if arnk > hrnk: + upset = True + else: + upset = False + elif ascr < hscr: + hstr = '{} {}'.format(self._bold(home), self._bold(hscr)) + astr = '{} {}'.format(away, ascr) + if hrnk > arnk: + upset = True + else: + upset = False + else: + astr = '{} {}'.format(away, ascr) + hstr = '{} {}'.format(home, hscr) + upset = False + + print(upset) + return astr, hstr, upset + + if len(games) == 2: + if games[0]['away'] == games[1]['away']: + for idx, game in enumerate(games): + if game['shortDetail'] == 'Postponed': + games.pop(idx) + + single = True if len(games) == 1 else False + + for game in games: + string = '' + if single: + away = game['away_full'] + home = game['home_full'] + time = game['date'] + else: + away = game['away'] + home = game['home'] + time = game['time'] + if game['status'] == 'pre': + string = '{} @ {} - {}'.format(away, home, time) + if single: + string += ' - \x02TV:\x02 {}'.format(game['broadcasts']) + if game['odds']: + string += ' - \x02Odds:\x02 {}'.format(game['odds']) + tmp_strings.append(string) + elif game['in_progress']: + if not game['HT']: + if game['pos'] == game['away_id']: + if game['rz']: + away = self._red('<>{}'.format(away)) + else: + away = '<>' + away + if game['pos'] == game['home_id']: + if game['rz']: + home = self._red('<>{}'.format(home)) + else: + home = '<>' + home + away_str, home_str, upset = _parseScores(away, game['a_score'], home, game['h_score'], game['away_team_rank'], game['home_team_rank']) + if game['clock'] == 'HT': + if single: + game['clock'] = self._orange('Halftime') + else: + game['clock'] = self._orange(game['clock']) + if single: + game['clock'] = '({})'.format(game['clock']) + string = '{} @ {} {}'.format(away_str, home_str, game['clock']) + if upset: + if single: + string += ' :: {}'.format(self._orange('UPSET')) + string = self._ul(string) + if not game['HT'] and single: + if game['desc']: + desc = ' :: {}'.format(game['desc']) + else: + desc = '' + if game['last_play']: + string += '{} :: \x02Last Play:\x02 {}'.format(desc, game['last_play']) + if game['broadcasts']: + string += ' :: \x02TV:\x02 {}'.format(game['broadcasts']) + tmp_strings.append(string) + elif game['status'] == 'post': + #print(game['shortDetail']) + if game['shortDetail'] != 'Final': + endCode = game['shortDetail'] + if 'Postponed' in endCode: + if not single: + endCode = self._red('PPD') + else: + endCode = self._red(endCode) + elif 'Canceled' in endCode: + if not single: + endCode = self._red('C') + else: + endCode = self._red(endCode) + elif 'OT' in endCode: + if single: + endCode = self._red(endCode) + else: + endCode = self._red('F/OT') + elif 'OT' in game['shortDetail']: + if single: + endCode = self._red(endCode) + else: + endCode = self._red('F/OT') + else: + if single: + endCode = self._red('Final') + else: + endCode = self._red('F') + away_str, home_str, _ = _parseScores(away, game['a_score'], home, game['h_score'], game['away_team_rank'], game['home_team_rank']) + string = '{} @ {} {}'.format(away_str, home_str, endCode) + tmp_strings.append(string) + + + print(len(tmp_strings), half_point) + if len(tmp_strings) > 1 and half_point >= 6: + reply_strings.append(' | '.join(string for string in tmp_strings[:half_point])) + reply_strings.append(' | '.join(string for string in tmp_strings[half_point:])) + else: + reply_strings.append(' | '.join(string for string in tmp_strings)) + + + return reply_strings + + def _red(self, string): + """Returns a red string.""" + return ircutils.mircColor(string, 'red') + + def _yellow(self, string): + """Returns a yellow string.""" + return ircutils.mircColor(string, 'yellow') + + def _orange(self, string): + return ircutils.mircColor(string, 'orange') + + def _green(self, string): + """Returns a green string.""" + return ircutils.mircColor(string, 'green') + + def _blue(self, string): + """Returns a blue string.""" + return ircutils.mircColor(string, 'blue') + + def _bold(self, string): + """Returns a bold string.""" + return ircutils.bold(string) + + def _ul(self, string): + """Returns an underline string.""" + return ircutils.underline(string) + + def _bu(self, string): + """Returns a bold/underline string.""" + return ircutils.bold(ircutils.underline(string)) + + +Class = CFBScores + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/test.py b/test.py new file mode 100644 index 0000000..17c3915 --- /dev/null +++ b/test.py @@ -0,0 +1,15 @@ +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +from supybot.test import * + + +class CFBScoresTestCase(PluginTestCase): + plugins = ('CFBScores',) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: From d9cb1ac07dddd69782675f879ca5849524c19854 Mon Sep 17 00:00:00 2001 From: cottongin Date: Thu, 1 Nov 2018 13:17:55 -0500 Subject: [PATCH 2/4] cleanup and adds some commands --- plugin.py | 91 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 21 deletions(-) diff --git a/plugin.py b/plugin.py index 60d6ad7..98f2359 100644 --- a/plugin.py +++ b/plugin.py @@ -10,6 +10,8 @@ import requests import pendulum import json import html +from bs4 import BeautifulSoup +import re from supybot import utils, plugins, ircutils, callbacks, schedule from supybot.commands import * @@ -36,13 +38,19 @@ class CFBScores(callbacks.Plugin): self.FUZZY_DAYS = ['yesterday', 'tonight', 'today', 'tomorrow', 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'] + self._current_week = 0 + self._cfb_byes = {} + with open("abbrv.json", 'r') as json_file: self.abbrv = json.load(json_file) + + if not self.abbrv: + self.abbrv = requests.get( + 'https://raw.githubusercontent.com/diagonalfish/FootballBotX2/master/abbrv.json') + self.abbrv = self.abbrv.json() def periodicCheckGames(): - #print('Periodic check') self.CFB_GAMES = self._fetchGames(None, '') - #print(self.CFB_GAMES) periodicCheckGames() @@ -55,11 +63,50 @@ class CFBScores(callbacks.Plugin): pass schedule.addPeriodicEvent(periodicCheckGames, 20, now=False, name='fetchCFBscores') - @wrap([optional('text')]) - def cfbrankings(self, irc, msg, args, filter_team=None): + @wrap + def cfbbyes(self, irc, msg, args): + """Gets teams on bye week for current week""" + + url = 'https://247sports.com/Article/Schedule-of-bye-weeks-for-college-footballs-top-2018-contenders-120880121/' + headers = {'User-Agent': 'Mozilla/5.0 (X11; CrOS x86_64 11151.4.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.8 Safari/537.36'} + + if not self._cfb_byes: + data = requests.get(url, headers=headers) + soup = BeautifulSoup(data.content) + + pattern = re.compile(r'Week') + section = soup.find('section', class_='article-body') + ps = section.find_all('p') + + output = {} + for p in ps: + if p.text.startswith('Week'): + week = p.text.split(':')[0] + week = week.split('(')[0].strip() + byes = p.text.split(':')[1].strip() + if byes: + output[week] = byes + + self._cfb_byes = output + + try: + irc.reply(self._cfb_byes[self._current_week]) + return + except: + irc.reply('No teams on bye this week') + return + + + @wrap([getopts({'week': 'positiveInt'}), optional('text')]) + def cfbrankings(self, irc, msg, args, optlist, filter_team=None): """Fetches CFB Rankings""" + optlist = dict(optlist) + week = optlist.get('week') + url = 'http://site.api.espn.com/apis/site/v2/sports/football/college-football/rankings' + if week: + url += '?weeks={}'.format(week) try: data = requests.get(url) @@ -68,11 +115,17 @@ class CFBScores(callbacks.Plugin): irc.reply('Error fetching rankings') return - rankings = data['rankings'][0] - week = ' ' + data['latestWeek']['displayValue'] - title = data['rankings'][0]['name'] + if not week: + week = ' ' + data['latestWeek']['displayValue'] + else: + if week > data['latestWeek']['number']: + irc.reply("Sorry, I cannot predict the future") + return + week = ' Week {}'.format(week) - # #1 ALA (3-0 | -) + rankings = data['rankings'][0] + self._current_week = week.strip() + title = data['rankings'][0]['name'] output = [] for team in rankings['ranks']: @@ -93,6 +146,7 @@ class CFBScores(callbacks.Plugin): if filter_team: if filter_team.upper() == team['team']['abbreviation']: + tmp += ' :: {} points'.format(team['points']) output.append(tmp) break else: @@ -125,7 +179,6 @@ class CFBScores(callbacks.Plugin): self.CFB_GAMES = self._fetchGames(team, conf, week) if not self.CFB_GAMES: - #print('Pulling fresh') self.CFB_GAMES = self._fetchGames(team, conf) if not self.CFB_GAMES: irc.reply('No games found') @@ -136,7 +189,6 @@ class CFBScores(callbacks.Plugin): reply_string = self._replyAsString(team, games) - #print(reply_string) for string in reply_string: irc.reply(string) @@ -165,7 +217,6 @@ class CFBScores(callbacks.Plugin): if week: url += '&week={}'.format(week) - #print(url) games = requests.get(url) games = games.json() @@ -178,14 +229,11 @@ class CFBScores(callbacks.Plugin): for game in games: if team == game['competitions'][0]['competitors'][0]['team']['abbreviation'] \ or team == game['competitions'][0]['competitors'][1]['team']['abbreviation']: - #or team in html.unescape(game['competitions'][0]['competitors'][0]['team']['location']).upper() \ - #or team in html.unescape(game['competitions'][0]['competitors'][1]['team']['location']).upper(): ngames.append(game) if not ngames: if team == 'HAWAII': team = "HAWAI'I" for game in games: - #print(team) if team == html.unescape(game['competitions'][0]['competitors'][0]['team']['location']).upper() \ or team == html.unescape(game['competitions'][0]['competitors'][1]['team']['location']).upper(): ngames.append(game) @@ -202,8 +250,6 @@ class CFBScores(callbacks.Plugin): for idx, game in enumerate(games): if team.upper() == game['competitions'][0]['competitors'][0]['team']['abbreviation'] \ or team.upper() == game['competitions'][0]['competitors'][1]['team']['abbreviation']: - #or team in html.unescape(game['competitions'][0]['competitors'][0]['team']['location']).upper() \ - #or team in html.unescape(game['competitions'][0]['competitors'][1]['team']['location']).upper(): games = [games.pop(idx)] break @@ -235,7 +281,6 @@ class CFBScores(callbacks.Plugin): new_game['odds'] = '' print(e) - #print(new_game['odds']) if new_game['status'] == 'in' and not new_game['final']: new_game['in_progress'] = True @@ -283,9 +328,7 @@ class CFBScores(callbacks.Plugin): def _replyAsString(self, team, games): reply_strings = [] tmp_strings = [] - #print(games[0]) half_point = len(games)//2 - #print(half_point) def _parseScores(away, ascr, home, hscr, arnk, hrnk): print(ascr, arnk, hscr, hrnk) @@ -370,9 +413,10 @@ class CFBScores(callbacks.Plugin): string += '{} :: \x02Last Play:\x02 {}'.format(desc, game['last_play']) if game['broadcasts']: string += ' :: \x02TV:\x02 {}'.format(game['broadcasts']) + if game['odds']: + string += ' :: \x02Odds:\x02 {}'.format(game['odds']) tmp_strings.append(string) elif game['status'] == 'post': - #print(game['shortDetail']) if game['shortDetail'] != 'Final': endCode = game['shortDetail'] if 'Postponed' in endCode: @@ -400,8 +444,13 @@ class CFBScores(callbacks.Plugin): endCode = self._red('Final') else: endCode = self._red('F') - away_str, home_str, _ = _parseScores(away, game['a_score'], home, game['h_score'], game['away_team_rank'], game['home_team_rank']) + away_str, home_str, upset = _parseScores(away, game['a_score'], home, game['h_score'], game['away_team_rank'], game['home_team_rank']) string = '{} @ {} {}'.format(away_str, home_str, endCode) + if upset and not single: + string = self._ul(string) + if single: + if upset: + string += ' :: {}'.format(self._orange('UPSET')) tmp_strings.append(string) From 621c12b99e0be57bef2a3e9d78166157619290b9 Mon Sep 17 00:00:00 2001 From: cottongin Date: Thu, 1 Nov 2018 13:21:49 -0500 Subject: [PATCH 3/4] cleanup help text --- plugin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugin.py b/plugin.py index 98f2359..a124181 100644 --- a/plugin.py +++ b/plugin.py @@ -162,7 +162,11 @@ class CFBScores(callbacks.Plugin): @wrap([getopts({'week': 'positiveInt', 'conf': 'positiveInt'}), optional('text')]) def cfb(self, irc, msg, args, optlist, team=None): - """[--week #] []""" + """[--conf #] [--week #] [] + Fetches CFB Scores. Defaults to current week and AP Top 25 teams. + Use --conf # (ESPN league #) to fetch a specific conference. + Use --week # to look up a specific week. + """ optlist = dict(optlist) week = optlist.get('week') From adba9a01db52c428902c61da291345824358b4e7 Mon Sep 17 00:00:00 2001 From: oddluck Date: Thu, 5 Dec 2019 09:45:11 +0000 Subject: [PATCH 4/4] Initial commit. --- README.md => CFBScores/README.md | 0 __init__.py => CFBScores/__init__.py | 9 ++++++--- abbrv.json => CFBScores/abbrv.json | 0 config.py => CFBScores/config.py | 0 plugin.py => CFBScores/plugin.py | 4 ++-- CFBScores/requirements.txt | 3 +++ test.py => CFBScores/test.py | 0 local/__init__.py | 1 - 8 files changed, 11 insertions(+), 6 deletions(-) rename README.md => CFBScores/README.md (100%) rename __init__.py => CFBScores/__init__.py (75%) rename abbrv.json => CFBScores/abbrv.json (100%) rename config.py => CFBScores/config.py (100%) rename plugin.py => CFBScores/plugin.py (99%) create mode 100644 CFBScores/requirements.txt rename test.py => CFBScores/test.py (100%) delete mode 100644 local/__init__.py diff --git a/README.md b/CFBScores/README.md similarity index 100% rename from README.md rename to CFBScores/README.md diff --git a/__init__.py b/CFBScores/__init__.py similarity index 75% rename from __init__.py rename to CFBScores/__init__.py index 9a39a68..793d593 100644 --- a/__init__.py +++ b/CFBScores/__init__.py @@ -6,7 +6,7 @@ ### """ -CFBScores: Fetches CFB scores +CFBScores: Fetches College Football scores """ import sys @@ -18,14 +18,17 @@ from supybot import world __version__ = "" # XXX Replace this with an appropriate author or supybot.Author instance. -__author__ = supybot.authors.unknown +__author__ = supybot.Author('cottongin', 'cottongin', + 'cottongin@cottongin.club') +__maintainer__ = getattr(supybot.authors, 'oddluck', + supybot.Author('oddluck', 'oddluck', 'oddluck@riseup.net')) # This is a dictionary mapping supybot.Author instances to lists of # contributions. __contributors__ = {} # This is a url where the most recent plugin package can be downloaded. -__url__ = '' +__url__ = 'https://github.com/oddluck/limnoria-plugins/' from . import config from . import plugin diff --git a/abbrv.json b/CFBScores/abbrv.json similarity index 100% rename from abbrv.json rename to CFBScores/abbrv.json diff --git a/config.py b/CFBScores/config.py similarity index 100% rename from config.py rename to CFBScores/config.py diff --git a/plugin.py b/CFBScores/plugin.py similarity index 99% rename from plugin.py rename to CFBScores/plugin.py index a124181..4c99f74 100644 --- a/plugin.py +++ b/CFBScores/plugin.py @@ -12,7 +12,7 @@ import json import html from bs4 import BeautifulSoup import re - +import os from supybot import utils, plugins, ircutils, callbacks, schedule from supybot.commands import * try: @@ -41,7 +41,7 @@ class CFBScores(callbacks.Plugin): self._current_week = 0 self._cfb_byes = {} - with open("abbrv.json", 'r') as json_file: + with open("{0}/abbrv.json".format(os.path.dirname(os.path.abspath(__file__))), 'r') as json_file: self.abbrv = json.load(json_file) if not self.abbrv: diff --git a/CFBScores/requirements.txt b/CFBScores/requirements.txt new file mode 100644 index 0000000..a51a020 --- /dev/null +++ b/CFBScores/requirements.txt @@ -0,0 +1,3 @@ +requests +pendulum +beautifulsoup4 \ No newline at end of file diff --git a/test.py b/CFBScores/test.py similarity index 100% rename from test.py rename to CFBScores/test.py diff --git a/local/__init__.py b/local/__init__.py deleted file mode 100644 index e86e97b..0000000 --- a/local/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Stub so local is a module, used for third-party modules