Merge branch 'master' SoccerScores

This commit is contained in:
oddluck 2019-12-05 09:55:11 +00:00
commit 6b21cfe15f
6 changed files with 427 additions and 0 deletions

1
SoccerScores/README.md Normal file
View File

@ -0,0 +1 @@
Fetches soccer scores and other information

52
SoccerScores/__init__.py Normal file
View File

@ -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:

33
SoccerScores/config.py Normal file
View File

@ -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:

324
SoccerScores/plugin.py Normal file
View File

@ -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&region=us&'
'dates={date}&league={league}')
# http://site.api.espn.com/apis/site/v2/sports/soccer/eng.2/scoreboard
# ?lang=en&region=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:

View File

@ -0,0 +1,2 @@
requests
pendulum

15
SoccerScores/test.py Normal file
View File

@ -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: