From cc79d387c3886f7c5c35e120e0012a7fe644b169 Mon Sep 17 00:00:00 2001 From: cottongin Date: Thu, 28 Feb 2019 09:34:46 -0600 Subject: [PATCH 1/2] initial commit --- README.md | 1 + __init__.py | 49 ++++++++ config.py | 33 +++++ plugin.py | 347 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test.py | 15 +++ 5 files changed, 445 insertions(+) create mode 100644 README.md create mode 100644 __init__.py create mode 100644 config.py create mode 100644 plugin.py create mode 100644 test.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1d4271 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Fetches odds diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..abfdaad --- /dev/null +++ b/__init__.py @@ -0,0 +1,49 @@ +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +""" +NewOdds: Fetches odds +""" + +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/config.py b/config.py new file mode 100644 index 0000000..0748585 --- /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('NewOdds') +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('NewOdds', True) + + +NewOdds = conf.registerPlugin('NewOdds') +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(NewOdds, 'someConfigVariableName', +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..3fe29f6 --- /dev/null +++ b/plugin.py @@ -0,0 +1,347 @@ +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +import requests +import pendulum + +from supybot import utils, plugins, ircutils, callbacks +from supybot.commands import * +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('NewOdds') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +class NewOdds(callbacks.Plugin): + """Fetches odds""" + threaded = True + + def __init__(self, irc): + self.__parent = super(NewOdds, self) + self.__parent.__init__(irc) + + @wrap([getopts({'nfl': '', 'mlb': '', 'nhl': '', 'nba': '', + 'cfb': '', 'cbb': '', 'NFL': '', 'MLB': '', + 'NHL': '', 'NBA': '', 'CFB': '', 'CBB': ''}), + optional('text')]) + def odds(self, irc, msg, args, optlist, opttext=None): + """-- [] + Fetches odds for given league with an optional team to filter + """ + + optlist = dict(optlist) + #print(optlist) + validLeagues = ['--NFL', '--MLB', '--NHL', '--NBA', '--CFB', '--CBB'] + leagueMap = {'cfb': 1, 'nfl': 2, 'mlb': 3, 'nba': 4, + 'cbb': 5, 'nhl': 6} + idMap = {'1': 'CFB', '2': 'NFL', '3': 'MLB', '4': 'NBA', + '5': 'CBB', '6': 'NHL'} + + if not optlist: + irc.reply(('Error: Must provide a valid league. ' + 'Valid leagues are: {}'.format( + ', '.join(x for x in validLeagues)))) + return + + leagues = [] + outputLeagues = [] + for key, value in optlist.items(): + league = key.lower() if value == True else None + if league: + leagues.append(leagueMap[league]) + outputLeagues.append(idMap[str(leagueMap[league])]) + + base_url = 'https://therundown.io/api/v1/sports/{league}/events/{date}?offset=300' + data = [] + today = pendulum.now('US/Pacific').format('YYYY-MM-DD') + tdate = pendulum.now('US/Pacific') + + #if 6 in leagues: + # dates = [tdate.add(days=1).format('YYYY-MM-DD')] + for idx, league in enumerate(leagues): + #print(league) + if league == 6: + #print('one') + headers = {'accept': 'application/json, text/plain, */*', + 'accept-encoding': 'gzip, deflate, br', + 'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8', + 'origin': 'https://www.oddsshark.com/', + 'referer': 'https://www.oddsshark.com/nhl/odds', + 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'} + + data.append(requests.get('https://io.oddsshark.com/ticker/nhl', headers=headers).json()) + + elif league == 4: + #print('one') + headers = {'accept': 'application/json, text/plain, */*', + 'accept-encoding': 'gzip, deflate, br', + 'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8', + 'origin': 'https://www.oddsshark.com/', + 'referer': 'https://www.oddsshark.com/nba/odds', + 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'} + + data.append(requests.get('https://io.oddsshark.com/ticker/nba', headers=headers).json()) + else: + #print('two') + url = base_url.format(league=league, date=today) + print(url) + data.append(requests.get(url).json()) + + if league == 2 or league == 1 or league == 3: + if league == 2 or league == 1: + dates = [tdate.add(days=1).format('YYYY-MM-DD'), tdate.add(days=2).format('YYYY-MM-DD'), + tdate.add(days=3).format('YYYY-MM-DD'), tdate.add(days=4).format('YYYY-MM-DD'), + tdate.add(days=5).format('YYYY-MM-DD'), tdate.add(days=6).format('YYYY-MM-DD')] + if league == 3: + dates = [tdate.add(days=1).format('YYYY-MM-DD'), tdate.add(days=2).format('YYYY-MM-DD'), + tdate.add(days=3).format('YYYY-MM-DD'), tdate.add(days=4).format('YYYY-MM-DD')] +# if league == 6: +# dates + for nflday in dates: + url = base_url.format(league=league, date=nflday) + #print(url) + #tdata = requests.get(url).json() + data[idx]['events'].extend(requests.get(url).json()['events']) + #print(data) +# +#try: +# print(data[0], data[1]) +# except: +# pass + + cleaned_data = self._parseData(data, opttext, leagues) + + if not cleaned_data: + irc.reply('Error: No odds found for that query') + return + + cleaned_data = self._sortData(cleaned_data) + + #print(outputLeagues) + for idx, item in enumerate(cleaned_data): + if item: + if 'Games with' not in item: + if len(item) > 8: + if len(item) > 16: + split = len(item)//3 + irc.reply('\x02{}:\x02 {}'.format(outputLeagues[idx], ' | '.join(x for x in item[:split]))) + irc.reply('{}'.format(' | '.join(x for x in item[split:split+split]))) + irc.reply('{}'.format(' | '.join(x for x in item[split+split:]))) + return + split = len(item)//2 + irc.reply('\x02{}:\x02 {}'.format(outputLeagues[idx], ' | '.join(x for x in item[:split]))) + irc.reply('{}'.format(' | '.join(x for x in item[split:]))) + else: + irc.reply('\x02{}:\x02 {}'.format(outputLeagues[idx], ' | '.join(x for x in item))) + else: + irc.reply(item) + else: + irc.reply('\x02{}:\x02 {}'.format(outputLeagues[idx], 'No results found for that query')) + + def _parseData(self, data, team, leagues, tz=None): + + new_data = [] + + #print(len(data['events'])) + #print(len(data)) + + for idx, item in enumerate(data): + #print(leagues[idx]) + if leagues[idx] in [4, 6]: + if leagues[idx] == 6: + translate_team = { + 'WAS': 'WSH', 'MON': 'MTL', 'CLB': 'CBJ', + 'NJ': 'NJD', 'SJ': 'SJS', 'LA': 'LAK', + 'TB': 'TBL', 'NAS': 'NSH', 'CAL': 'CGY', + } + else: + translate_team = {'CHR': 'CHA', 'NY': 'NYK', 'SAN': 'SAS', 'PHO': 'PHX'} + #print(item) + today = pendulum.now() + tmp_data = [] + no_odds_str = 'Games with no odds yet: ' + no_odds = [] + for event in item['matchups']: + if event['type'] != 'matchup': + continue + date = pendulum.parse(event['event_date']).format('M/D h:mm A') + home = event['home_short_name'] if event['home_short_name'] not in translate_team else translate_team[event['home_short_name']] + away = event['away_short_name'] if event['away_short_name'] not in translate_team else translate_team[event['away_short_name']] + shome = home + saway = away + home_spread = '' + away_spread = '' + if event['away_odds']: + if float(event['away_odds']) < 0: + saway = ircutils.bold(away) + elif float(event['home_odds']) < 0: + shome = ircutils.bold(home) + ou = '-' if not event['total'] else event['total'] + + ml = '{}/{}'.format( + '-' if not event['away_odds'] else self._colorize(float(event['away_odds']), float(event['home_odds'])), + '-' if not event['home_odds'] else self._colorize(float(event['home_odds']), float(event['away_odds']))) + string = '{}{} @ {}{} ↕{} {} ({})'.format( + saway, away_spread, shome, home_spread, + ou, ml, date) + if ou == '-' and ml == '-/-' and (today.format('YYYY-MM-DD') in event['event_date'] or today.add(days=1).format('YYYY-MM-DD') in event['event_date']) and not team: + no_odds.append('{} @ {}'.format(away, home)) + if team: + if today.format('YYYY-MM-DD') in event['event_date'] or today.add(days=1).format('YYYY-MM-DD') in event['event_date']: + if team.lower() == home.lower() or team.lower() == away.lower(): + if ou != '-' and ml != '-/-': + tmp_data.append(string) + else: + if today.format('YYYY-MM-DD') in event['event_date'] or today.add(days=1).format('YYYY-MM-DD') in event['event_date']: + if ou != '-' and ml != '-/-': + tmp_data.append(string) + if tmp_data: +# if no_odds: +# tmp_data.append('{}{}'.format(no_odds_str, ', '.join(i for i in no_odds))) + new_data.append(tmp_data) + if no_odds: + new_data.append('{}{}'.format(no_odds_str, ', '.join(i for i in no_odds))) + else: + new_data.append(None) + else: + tmp_data = [] + #print(len(item['events'])) + for event in item['events']: + #aff_id = '7' # default id + aff_id = None + for aff in event['lines']: + if 'Bookmaker' in event['lines'][aff]['affiliate']['affiliate_name']: + aff_id = aff + if not aff_id: + for aff in event['lines']: + aff_id = aff + break + if leagues[idx] == 2 or leagues[idx] == 1 or leagues[idx] == 3: + if tz: + date = pendulum.parse(event['event_date']).in_tz(tz).format('M/D h:mm A zz') + else: + date = pendulum.parse(event['event_date']).in_tz('US/Eastern').format('M/D h:mm A') + else: + if tz: + date = pendulum.parse(event['event_date']).in_tz(tz).format('h:mm A zz') + else: + date = pendulum.parse(event['event_date']).in_tz('US/Eastern').format('h:mm A') + home = event['teams_normalized'][1]['abbreviation'] + away = event['teams_normalized'][0]['abbreviation'] + home_name = event['teams_normalized'][1]['name'] + away_name = event['teams_normalized'][0]['name'] + check_spread = False + eventdate = pendulum.parse(event['lines'][aff_id]['spread']['date_updated'], strict=False).int_timestamp + updated = pendulum.parse(event['event_date'], strict=False).int_timestamp + #print(eventdate, updated) + if pendulum.parse(event['lines'][aff_id]['spread']['date_updated'], strict=False).int_timestamp > pendulum.parse(event['event_date'], strict=False).int_timestamp: + if leagues[idx] == 3: + check_spread == True + if (leagues[idx] != 3 and team): + home_spread = '[{}]'.format( + self._parseSpread(event['lines'][aff_id]['spread']['point_spread_home'])) + away_spread = '[{}]'.format( + self._parseSpread(event['lines'][aff_id]['spread']['point_spread_away'])) + else: + home_spread = '' + away_spread = '' + saway = away + shome = home + if check_spread: + moneyline_away = event['lines'][aff_id]['spread']['point_spread_away_money'] + moneyline_home = event['lines'][aff_id]['spread']['point_spread_home_money'] + else: + moneyline_away = event['lines'][aff_id]['moneyline']['moneyline_away'] + moneyline_home = event['lines'][aff_id]['moneyline']['moneyline_home'] + + if moneyline_away < 0: + saway = ircutils.bold(away) + elif moneyline_home < 0: + shome = ircutils.bold(home) + #print(event['lines']) + ou = '-' if event['lines'][aff_id]['total']['total_over'] == 0.0001 else event['lines'][aff_id]['total']['total_over'] + ml = '{}/{}'.format( + self._colorize(moneyline_away, moneyline_home), + self._colorize(moneyline_home, moneyline_away)) + string = '{}{} @ {}{} ↕{} {} ({})'.format( + saway, away_spread, shome, home_spread, + ou, ml, date) + if team: + print(team.lower(), home_name.lower(), away_name.lower()) + if (team.lower() == home.lower() or team.lower() == away.lower()) or \ + (team.lower() == home_name.lower() or team.lower() == away_name.lower()): + if ou != '-' and ml != '-/-': + tmp_data.append(string) + else: + if ou != '-' and ml != '-/-': + tmp_data.append(string) + #print(tmp_data) + if tmp_data: + new_data.append(tmp_data) + else: + new_data.append(None) + + #print(new_data) + + return new_data + + def _sortData(self, data): + #print(data) + for item in data: + #print(item) + if item: + if 'Games with' not in item: + #tmp = None + #for game in item: + # if 'Games with' in game: + # tmp = item.pop() + item.sort(key=lambda x:pendulum.parse( + x.split('(')[1].replace(')',''), strict=False).int_timestamp) + #if tmp: + # item.append(tmp) + + return data + + def _parseSpread(self, number): + if number == 0.0001: + number = 0 + if number > 0: + number = '+{}'.format(number) + else: + number = number + + return number + + def _colorize(self, number, vs): + if number == 0.0001: + return '-' + if number > 0: + number = '+{}'.format(number) + number = ircutils.mircColor(number, 'red') + elif number < 0: + if vs < 0: + if number < vs: + number = '{}'.format(number) + number = ircutils.mircColor(number, 'green') + else: + number = '{}'.format(number) + number = ircutils.mircColor(number, 'red') + else: + number = '{}'.format(number) + number = ircutils.mircColor(number, 'green') + else: + number = number + + return number + +Class = NewOdds + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/test.py b/test.py new file mode 100644 index 0000000..e2bdad1 --- /dev/null +++ b/test.py @@ -0,0 +1,15 @@ +### +# Copyright (c) 2018, cottongin +# All rights reserved. +# +# +### + +from supybot.test import * + + +class NewOddsTestCase(PluginTestCase): + plugins = ('NewOdds',) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: From 1849561caf5bd010c193e8ecb3d6d9b66afe93c7 Mon Sep 17 00:00:00 2001 From: oddluck Date: Thu, 5 Dec 2019 09:52:21 +0000 Subject: [PATCH 2/2] Initial commit. --- README.md => NewOdds/README.md | 0 __init__.py => NewOdds/__init__.py | 7 +++++-- config.py => NewOdds/config.py | 0 plugin.py => NewOdds/plugin.py | 0 NewOdds/requirements.txt | 2 ++ test.py => NewOdds/test.py | 0 6 files changed, 7 insertions(+), 2 deletions(-) rename README.md => NewOdds/README.md (100%) rename __init__.py => NewOdds/__init__.py (77%) rename config.py => NewOdds/config.py (100%) rename plugin.py => NewOdds/plugin.py (100%) create mode 100644 NewOdds/requirements.txt rename test.py => NewOdds/test.py (100%) diff --git a/README.md b/NewOdds/README.md similarity index 100% rename from README.md rename to NewOdds/README.md diff --git a/__init__.py b/NewOdds/__init__.py similarity index 77% rename from __init__.py rename to NewOdds/__init__.py index abfdaad..948a6cc 100644 --- a/__init__.py +++ b/NewOdds/__init__.py @@ -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/config.py b/NewOdds/config.py similarity index 100% rename from config.py rename to NewOdds/config.py diff --git a/plugin.py b/NewOdds/plugin.py similarity index 100% rename from plugin.py rename to NewOdds/plugin.py diff --git a/NewOdds/requirements.txt b/NewOdds/requirements.txt new file mode 100644 index 0000000..92f1d59 --- /dev/null +++ b/NewOdds/requirements.txt @@ -0,0 +1,2 @@ +pendulum +requests \ No newline at end of file diff --git a/test.py b/NewOdds/test.py similarity index 100% rename from test.py rename to NewOdds/test.py