mirror of
https://github.com/oddluck/limnoria-plugins.git
synced 2025-04-26 04:51:09 -05:00
1. if this happens in the first loop, `UnboundLocalError: local variable 'headers' referenced before assignment` 2. `headers` was passed instead of `url`
567 lines
23 KiB
Python
567 lines
23 KiB
Python
###
|
|
# Copyright (c) 2018, cottongin
|
|
# Copyright (c) 2020, oddluck <oddluck@riseup.net>
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions are met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright notice,
|
|
# this list of conditions, and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
# this list of conditions, and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
# * Neither the name of the author of this software nor the name of
|
|
# contributors to this software may be used to endorse or promote products
|
|
# derived from this software without specific prior written consent.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
###
|
|
|
|
import requests
|
|
import pendulum
|
|
import json
|
|
from supybot import utils, plugins, ircutils, callbacks
|
|
from supybot.commands import *
|
|
|
|
try:
|
|
from supybot.i18n import PluginInternationalization
|
|
|
|
_ = PluginInternationalization("Odds")
|
|
except ImportError:
|
|
# Placeholder that allows to run the plugin on a bot
|
|
# without the i18n module
|
|
_ = lambda x: x
|
|
|
|
|
|
class Odds(callbacks.Plugin):
|
|
"""Fetches odds"""
|
|
|
|
threaded = True
|
|
|
|
def __init__(self, irc):
|
|
self.__parent = super(Odds, 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):
|
|
"""--<league> [<team>]
|
|
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"
|
|
),
|
|
}
|
|
|
|
request = requests.get(
|
|
"https://io.oddsshark.com/ticker/nhl", headers=headers
|
|
)
|
|
request = json.loads(request.content)
|
|
data.append(request)
|
|
|
|
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"
|
|
),
|
|
}
|
|
|
|
request = requests.get(
|
|
"https://io.oddsshark.com/ticker/nba", headers=headers
|
|
)
|
|
request = json.loads(request.content)
|
|
data.append(request)
|
|
else:
|
|
# print('two')
|
|
url = base_url.format(league=league, date=today)
|
|
|
|
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/",
|
|
"user-agent": (
|
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML,"
|
|
" like Gecko) Chrome/69.0.3497.100 Safari/537.36"
|
|
),
|
|
}
|
|
|
|
request = requests.get(url, timeout=10, headers=headers)
|
|
request = json.loads(request.content)
|
|
data.append(request)
|
|
|
|
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)
|
|
request = requests.get(url)
|
|
request = json.loads(request.content)
|
|
data[idx]["events"].extend(request["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 = Odds
|
|
|
|
|
|
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|