Merge branch 'master' SoccerScores
This commit is contained in:
commit
6b21cfe15f
|
@ -0,0 +1 @@
|
|||
Fetches soccer scores and other information
|
|
@ -0,0 +1,52 @@
|
|||
###
|
||||
# Copyright (c) 2018, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
"""
|
||||
SoccerScores: Fetches soccer scores and other information
|
||||
"""
|
||||
|
||||
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.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__ = 'https://github.com/oddluck/limnoria-plugins/'
|
||||
|
||||
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:
|
|
@ -0,0 +1,33 @@
|
|||
###
|
||||
# Copyright (c) 2018, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
from supybot import conf, registry
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('SoccerScores')
|
||||
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('SoccerScores', True)
|
||||
|
||||
|
||||
Soccer = conf.registerPlugin('SoccerScores')
|
||||
# This is where your configuration variables (if any) should go. For example:
|
||||
# conf.registerGlobalValue(Soccer, 'someConfigVariableName',
|
||||
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
|
@ -0,0 +1,324 @@
|
|||
# Soccer
|
||||
###
|
||||
# Copyright (c) 2018, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
# See LICENSE.txt
|
||||
#
|
||||
###
|
||||
|
||||
from supybot import utils, plugins, ircutils, callbacks, schedule, conf
|
||||
from supybot.commands import *
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('SoccerScores')
|
||||
except ImportError:
|
||||
# Placeholder that allows to run the plugin on a bot
|
||||
# without the i18n module
|
||||
_ = lambda x: x
|
||||
|
||||
# Non-supybot imports
|
||||
import requests
|
||||
import pendulum
|
||||
import pickle
|
||||
|
||||
|
||||
class SoccerScores(callbacks.Plugin):
|
||||
"""Fetches soccer scores and other information"""
|
||||
threaded = True
|
||||
|
||||
def __init__(self, irc):
|
||||
self.__parent = super(SoccerScores, self)
|
||||
self.__parent.__init__(irc)
|
||||
|
||||
self.PICKLEFILE = conf.supybot.directories.data.dirize("soccer-leagues.db")
|
||||
|
||||
self.BASE_API_URL = ('http://site.api.espn.com/apis/site/v2/sports/'
|
||||
'soccer/{league}/scoreboard?lang=en®ion=us&'
|
||||
'dates={date}&league={league}')
|
||||
# http://site.api.espn.com/apis/site/v2/sports/soccer/eng.2/scoreboard
|
||||
# ?lang=en®ion=us&calendartype=whitelist
|
||||
# &limit=100&dates=20181028&league=eng.2
|
||||
|
||||
self.FUZZY_DAYS = ['yesterday', 'tonight', 'today', 'tomorrow',
|
||||
'sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
|
||||
|
||||
try:
|
||||
with open(self.PICKLEFILE, 'rb') as handle:
|
||||
self.LEAGUE_MAP = pickle.load(handle)
|
||||
except:
|
||||
self.LEAGUE_MAP = {
|
||||
'epl': 'eng.1', 'mls': 'usa.1', 'ecl': 'eng.2', 'uefac': 'uefa.champions',
|
||||
'uefae': 'uefa.europa', 'efac': 'eng.fa', 'carabao': 'eng.league_cup',
|
||||
'liga': 'esp.1', 'bundesliga': 'ger.1', 'seriea': 'ita.1', 'ligue': 'fra.1',
|
||||
'bbva': 'mex.1', 'fifawc': 'fifa.world', 'wc': 'fifa.world', 'nations': 'uefa.nations',
|
||||
'concacaf': 'concacaf.nations.league_qual', 'africa': 'caf.nations_qual',
|
||||
'cl': 'eng.2',
|
||||
}
|
||||
|
||||
# TO-DO / think about:
|
||||
"""
|
||||
def periodicCheckGames():
|
||||
self.CFB_GAMES = self._fetchGames(None, '')
|
||||
|
||||
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')
|
||||
"""
|
||||
def _dumpDB(self, db):
|
||||
with open(self.PICKLEFILE, 'wb') as handle:
|
||||
pickle.dump(db, handle, protocol=pickle.HIGHEST_PROTOCOL)
|
||||
return
|
||||
|
||||
@wrap(['admin','text'])
|
||||
def addleague(self, irc, msg, args, league):
|
||||
"""<nickname> <espn league>
|
||||
Adds <espn league> to bot's leagues database"""
|
||||
|
||||
league = league.lower()
|
||||
league = league.split()
|
||||
|
||||
if len(league) > 2:
|
||||
return
|
||||
if league[0] in self.LEAGUE_MAP:
|
||||
irc.reply('Already in database')
|
||||
return
|
||||
self.LEAGUE_MAP[league[0]] = league[1]
|
||||
self._dumpDB(self.LEAGUE_MAP)
|
||||
irc.replySuccess()
|
||||
return
|
||||
|
||||
@wrap(['admin','text'])
|
||||
def remleague(self, irc, msg, args, league):
|
||||
"""<nickname>
|
||||
Removes <nickname> from bot's leagues database"""
|
||||
|
||||
league = league.lower()
|
||||
league_t = league.split()
|
||||
|
||||
if len(league_t) > 1:
|
||||
return
|
||||
if league not in self.LEAGUE_MAP:
|
||||
irc.reply('Not found in database')
|
||||
return
|
||||
self.LEAGUE_MAP.pop(league, None)
|
||||
self._dumpDB(self.LEAGUE_MAP)
|
||||
irc.replySuccess()
|
||||
return
|
||||
|
||||
@wrap([getopts({'date': 'somethingWithoutSpaces',
|
||||
'league': 'somethingWithoutSpaces',
|
||||
'tz': 'somethingWithoutSpaces'}), optional('text')])
|
||||
def soccer(self, irc, msg, args, options, filter_team=None):
|
||||
"""--league <league> (--date <date>) (team)
|
||||
Fetches soccer scores for given team on date in provided league, defaults to current
|
||||
day if no date is provided and all teams in league if no team provided.
|
||||
"""
|
||||
|
||||
now = pendulum.now()
|
||||
today = now.in_tz('US/Eastern').format('YYYYMMDD')
|
||||
|
||||
options = dict(options)
|
||||
date = options.get('date')
|
||||
league = options.get('league')
|
||||
tz = options.get('tz') or 'US/Eastern'
|
||||
|
||||
if date:
|
||||
date = self._parseDate(date)
|
||||
date = pendulum.parse(date, strict=False).format('YYYYMMDD')
|
||||
else:
|
||||
date = today
|
||||
|
||||
if filter_team:
|
||||
filter_team = filter_team.lower()
|
||||
if filter_team in self.LEAGUE_MAP and not league:
|
||||
league = self.LEAGUE_MAP[filter_team]
|
||||
filter_team = None
|
||||
|
||||
if not league:
|
||||
irc.reply('ERROR: You must provide a league via --league <league>')
|
||||
doc = irc.getCallback('SoccerScores').soccer.__doc__
|
||||
doclines = doc.splitlines()
|
||||
s = '%s' % (doclines.pop(0))
|
||||
if doclines:
|
||||
help = ' '.join(doclines)
|
||||
s = '(%s) -- %s' % (ircutils.bold(s), help)
|
||||
s = utils.str.normalizeWhitespace(s)
|
||||
irc.reply(s)
|
||||
vl = ', '.join(k for k in self.LEAGUE_MAP)
|
||||
irc.reply('Valid leagues: {}'.format(vl))
|
||||
return
|
||||
|
||||
mapped_league = self.LEAGUE_MAP.get(league.lower())
|
||||
|
||||
if not mapped_league and '.' not in league:
|
||||
irc.reply('ERROR: {} not found in valid leagues: {}'.format(
|
||||
league, ', '.join(k for k in self.LEAGUE_MAP)))
|
||||
return
|
||||
elif not mapped_league:
|
||||
mapped_league = league.lower()
|
||||
|
||||
url = self.BASE_API_URL.format(date=date, league=mapped_league)
|
||||
|
||||
try:
|
||||
data = requests.get(url)
|
||||
except:
|
||||
irc.reply('Something went wrong fetching data from {}'.format(
|
||||
data.url))
|
||||
return
|
||||
|
||||
print(data.url)
|
||||
data = data.json()
|
||||
|
||||
if 'leagues' not in data:
|
||||
irc.reply('ERROR: {} not found in valid leagues: {}'.format(
|
||||
league, ', '.join(k for k in self.LEAGUE_MAP)))
|
||||
return
|
||||
|
||||
league_name = ircutils.bold(data['leagues'][0]['name'])
|
||||
|
||||
if not data['events']:
|
||||
irc.reply('No matches found')
|
||||
return
|
||||
|
||||
comps = []
|
||||
for event in data['events']:
|
||||
comps.append(event['competitions'][0])
|
||||
|
||||
#print(comps)
|
||||
single = False
|
||||
if len(comps) == 1:
|
||||
single = True
|
||||
matches = []
|
||||
for match in comps:
|
||||
#print(match)
|
||||
time = pendulum.parse(match['date'], strict=False).in_tz(tz).format('h:mm A zz')
|
||||
long_time = pendulum.parse(match['date'], strict=False).in_tz(tz).format('ddd MMM Do h:mm A zz')
|
||||
teams_abbr = [match['competitors'][0]['team']['abbreviation'].lower(),
|
||||
match['competitors'][1]['team']['abbreviation'].lower()]
|
||||
for team in match['competitors']:
|
||||
if team['homeAway'] == 'home':
|
||||
home = team['team']['shortDisplayName']
|
||||
home_abbr = team['team']['abbreviation']
|
||||
home_score = team['score']
|
||||
elif team['homeAway'] == 'away':
|
||||
away = team['team']['shortDisplayName']
|
||||
away_abbr = team['team']['abbreviation']
|
||||
away_score = team['score']
|
||||
clock = match['status']['displayClock']
|
||||
final = match['status']['type']['completed']
|
||||
status = match['status']['type']['shortDetail']
|
||||
if final:
|
||||
status = ircutils.mircColor(status, 'red')
|
||||
if status == 'HT':
|
||||
status = ircutils.mircColor(status, 'orange')
|
||||
state = match['status']['type']['state']
|
||||
|
||||
if state == 'pre':
|
||||
#
|
||||
if not filter_team and not single:
|
||||
string = '{1} - {0} {2}'.format(away_abbr, home_abbr, time)
|
||||
else:
|
||||
string = '{1} - {0}, {2}'.format(away, home, long_time)
|
||||
elif state == 'in':
|
||||
#
|
||||
if away_score > home_score:
|
||||
away = ircutils.bold(away)
|
||||
away_abbr = ircutils.bold(away_abbr)
|
||||
away_score = ircutils.bold(away_score)
|
||||
elif home_score > away_score:
|
||||
home = ircutils.bold(home)
|
||||
home_abbr = ircutils.bold(home_abbr)
|
||||
home_score = ircutils.bold(home_score)
|
||||
if not filter_team and not single:
|
||||
string = '{2} {3}-{1} {0} {4}'.format(away_abbr, away_score, home_abbr, home_score, clock)
|
||||
else:
|
||||
string = '{2} {3}-{1} {0} {4}'.format(away, away_score, home, home_score, clock)
|
||||
elif state == 'post':
|
||||
#
|
||||
if away_score > home_score:
|
||||
away = ircutils.bold(away)
|
||||
away_abbr = ircutils.bold(away_abbr)
|
||||
away_score = ircutils.bold(away_score)
|
||||
elif home_score > away_score:
|
||||
home = ircutils.bold(home)
|
||||
home_abbr = ircutils.bold(home_abbr)
|
||||
home_score = ircutils.bold(home_score)
|
||||
if not filter_team and not single:
|
||||
string = '{2} {3}-{1} {0} {4}'.format(away_abbr, away_score, home_abbr, home_score, status)
|
||||
else:
|
||||
string = '{2} {3}-{1} {0} {4}'.format(away, away_score, home, home_score, status)
|
||||
else:
|
||||
if not filter_team and not single:
|
||||
string = '{1} - {0} {2}'.format(away_abbr, home_abbr, time)
|
||||
else:
|
||||
string = '{1} - {0}, {2}'.format(away, home, long_time)
|
||||
|
||||
if filter_team:
|
||||
#print(filter_team, string)
|
||||
if filter_team in string.lower() or filter_team in teams_abbr:
|
||||
matches.append(string)
|
||||
else:
|
||||
matches.append(string)
|
||||
|
||||
if not matches:
|
||||
irc.reply('No matches found')
|
||||
return
|
||||
|
||||
irc.reply('{}: {}'.format(league_name, ' | '.join(s for s in matches)))
|
||||
|
||||
return
|
||||
|
||||
|
||||
def _parseDate(self, string):
|
||||
"""parse date"""
|
||||
date = string[:3].lower()
|
||||
if date in self.FUZZY_DAYS or string.lower() in self.FUZZY_DAYS:
|
||||
if date == 'yes':
|
||||
date_string = pendulum.yesterday('US/Eastern').format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'tod' or date == 'ton':
|
||||
date_string = pendulum.now('US/Eastern').format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'tom':
|
||||
date_string = pendulum.tomorrow('US/Eastern').format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'sun':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.SUNDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'mon':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.MONDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'tue':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.TUESDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'wed':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.WEDNESDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'thu':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.THURSDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'fri':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.FRIDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
elif date == 'sat':
|
||||
date_string = pendulum.now('US/Eastern').next(pendulum.SATURDAY).format('YYYYMMDD')
|
||||
return date_string
|
||||
else:
|
||||
return string
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
|
||||
Class = SoccerScores
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
|
@ -0,0 +1,2 @@
|
|||
requests
|
||||
pendulum
|
|
@ -0,0 +1,15 @@
|
|||
###
|
||||
# Copyright (c) 2018, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
from supybot.test import *
|
||||
|
||||
|
||||
class SoccerTestCase(PluginTestCase):
|
||||
plugins = ('SoccerScores',)
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
Loading…
Reference in New Issue