mirror of
https://github.com/oddluck/limnoria-plugins.git
synced 2025-04-26 04:51:09 -05:00
Temporarily remove some plugins.
This commit is contained in:
parent
acafa13069
commit
82e5a2df7d
@ -1,15 +0,0 @@
|
||||
Forked from: https://github.com/ProgVal/Supybot-plugins/tree/master/Markovgen
|
||||
|
||||
Additions:
|
||||
|
||||
plugins.Markovgen.ignorePattern (set regex patterns for the bot to ignore)
|
||||
|
||||
plugins.Markovgen.stripPattern (set regex patterns for the bot to strip)
|
||||
|
||||
plugins.Markovgen.stripURL (determine if the bot will strip URLs)
|
||||
|
||||
plugins.Markovgen.ignoreNicks (list of nicks to ignore)
|
||||
|
||||
plugins.Markovgen.ignoreCommands (ignore commands sent to the bot)
|
||||
|
||||
plugins.Markovgen.stripFormatting (strip bold, underline, and color from messages)
|
@ -1,73 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2014, Valentin Lorentz
|
||||
# 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.
|
||||
|
||||
###
|
||||
|
||||
"""
|
||||
Add a description of the plugin (to be presented to the user inside the wizard)
|
||||
here. This should describe *what* the plugin does.
|
||||
"""
|
||||
|
||||
import supybot
|
||||
import supybot.world as 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__ = getattr(supybot.authors, 'progval',
|
||||
supybot.Author('Valentin Lorentz', 'ProgVal', 'progval@gmail.com'))
|
||||
__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
|
||||
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:
|
@ -1,88 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2014, Valentin Lorentz
|
||||
# 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 supybot.conf as conf
|
||||
import supybot.registry as registry
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('Markovgen')
|
||||
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('Markovgen', True)
|
||||
|
||||
|
||||
Markovgen = conf.registerPlugin('Markovgen')
|
||||
# This is where your configuration variables (if any) should go. For example:
|
||||
# conf.registerGlobalValue(Markovgen, 'someConfigVariableName',
|
||||
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
|
||||
conf.registerChannelValue(Markovgen, 'enable',
|
||||
registry.Boolean(False, _("""Determines whether the plugin is enabled
|
||||
on a channel. This defaults to False to avoid useless resources
|
||||
consumption.""")))
|
||||
conf.registerChannelValue(Markovgen, 'probability',
|
||||
registry.Probability(0, _("""Determine the probability the bot has to
|
||||
reply to a message.""")))
|
||||
conf.registerChannelValue(Markovgen, 'stripRelayedNick',
|
||||
registry.Boolean(True, _("""Determines whether the bot will strip
|
||||
strings like <XXX> at the beginning of messages.""")))
|
||||
conf.registerChannelValue(Markovgen, 'ignorePattern',
|
||||
registry.Regexp("", _("""Mesages matching this pattern will be ignored.""")))
|
||||
conf.registerChannelValue(Markovgen, 'stripPattern',
|
||||
registry.Regexp("", _("""Text matching this pattern will be stripped.""")))
|
||||
conf.registerChannelValue(Markovgen, 'stripURL',
|
||||
registry.Boolean(True, _("""Determines whether the bot will strip
|
||||
URLs from messages.""")))
|
||||
conf.registerChannelValue(Markovgen, 'ignoreNicks',
|
||||
registry.SpaceSeparatedListOfStrings([], _("""A list of nicks to be ignored by the bot""")))
|
||||
conf.registerChannelValue(Markovgen, 'stripFormatting',
|
||||
registry.Boolean(True, _("""Determines whether the bot will strip
|
||||
bold, underline, and colors from messages.""")))
|
||||
conf.registerChannelValue(Markovgen, 'ignoreCommands',
|
||||
registry.Boolean(True, _("""Determines whether the bot will ignore commands.""")))
|
||||
|
||||
conf.registerGroup(Markovgen, 'onNick')
|
||||
conf.registerChannelValue(Markovgen.onNick, 'probability',
|
||||
registry.Probability(0, _("""Determine the probability the bot has to
|
||||
reply to a message containing its nick.""")))
|
||||
conf.registerChannelValue(Markovgen.onNick, 'replaceNick',
|
||||
registry.Boolean(True, _("""Determine whether the bot will replace its
|
||||
nick by the original author's when replying to a message containing
|
||||
its nick.""")))
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
@ -1 +0,0 @@
|
||||
# Stub so local is a module, used for third-party modules
|
@ -1,255 +0,0 @@
|
||||
# coding: utf8
|
||||
###
|
||||
# Copyright (c) 2014, Valentin Lorentz
|
||||
# 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 os
|
||||
import re
|
||||
import sys
|
||||
import glob
|
||||
import random
|
||||
import functools
|
||||
|
||||
import supybot.conf as conf
|
||||
import supybot.world as world
|
||||
import supybot.utils as utils
|
||||
from supybot.commands import *
|
||||
import supybot.plugins as plugins
|
||||
import supybot.ircutils as ircutils
|
||||
import supybot.callbacks as callbacks
|
||||
import supybot.log as log
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('Markovgen')
|
||||
except ImportError:
|
||||
# Placeholder that allows to run the plugin on a bot
|
||||
# without the i18n module
|
||||
_ = lambda x:x
|
||||
|
||||
try:
|
||||
import markovgen
|
||||
except ImportError:
|
||||
raise callbacks.Error('Cannot load markovgen library. Make sure you '
|
||||
'installed it (%s -m pip install markovgen).'
|
||||
% sys.executable)
|
||||
from imp import reload as r
|
||||
r(markovgen)
|
||||
|
||||
MATCH_MESSAGE_STRIPNICK = re.compile('^(<[^ ]+> )?(?P<message>.*)$')
|
||||
|
||||
CHANNELLOGER_REGEXP_BASE = re.compile('^[^ ]* (<[^ ]+> )?(?P<message>.*)$')
|
||||
CHANNELLOGER_REGEXP_STRIPNICK = re.compile('^[^ ]* (<[^ ]+> )?(<[^ ]+> )?(?P<message>.*)$')
|
||||
|
||||
def get_channelloger_extracter(stripRelayedNick):
|
||||
@markovgen.mixed_encoding_extracting
|
||||
def channelloger_extracter(x):
|
||||
regexp = CHANNELLOGER_REGEXP_STRIPNICK if stripRelayedNick else \
|
||||
CHANNELLOGER_REGEXP_BASE
|
||||
m = regexp.match(x)
|
||||
if m:
|
||||
return m.group('message')
|
||||
return channelloger_extracter
|
||||
|
||||
def get_extracter(name):
|
||||
regexp = re.compile(markovgen.REGEXPS[name])
|
||||
@markovgen.mixed_encoding_extracting
|
||||
def extracter(x):
|
||||
msg = regexp.match(x)
|
||||
if msg:
|
||||
return msg.group('message')
|
||||
return extracter
|
||||
|
||||
def rec_list_files(path):
|
||||
return (os.path.join(dp, f)
|
||||
for dp, dn, filenames in os.walk(path)
|
||||
for f in filenames)
|
||||
|
||||
class Markovgen(callbacks.Plugin):
|
||||
"""Add the help for "@plugin help Markovgen" here
|
||||
This should describe *how* to use this plugin."""
|
||||
threaded = True
|
||||
|
||||
def __init__(self, irc):
|
||||
super(Markovgen, self).__init__(irc)
|
||||
self._markovs = {}
|
||||
|
||||
def _load_from_channellogger(self, irc, channel, m):
|
||||
cb = irc.getCallback('ChannelLogger')
|
||||
if not cb:
|
||||
return
|
||||
extracter = get_channelloger_extracter(
|
||||
self.registryValue('stripRelayedNick', channel))
|
||||
for irc in world.ircs:
|
||||
for filename in glob.glob(cb.getLogDir(irc, channel) + '/*.log'):
|
||||
with open(filename, 'rb') as fd:
|
||||
m.feed_from_file(fd, extracter)
|
||||
|
||||
def _load_from_data(self, irc, channel, m):
|
||||
base_path = os.path.join(conf.supybot.directories.data(), 'Markovgen', channel)
|
||||
if not os.path.isdir(base_path):
|
||||
return
|
||||
for extracter_name in os.listdir(base_path):
|
||||
extracter = get_extracter(extracter_name)
|
||||
path = os.path.join(base_path, extracter_name)
|
||||
path = glob.escape(path)
|
||||
filenames = rec_list_files(path)
|
||||
for filename in filenames:
|
||||
with open(filename, 'rb') as fd:
|
||||
m.feed_from_file(fd, extracter)
|
||||
|
||||
|
||||
def _get_markov(self, irc, channel):
|
||||
if channel not in self._markovs:
|
||||
m = markovgen.Markov()
|
||||
self._markovs[channel] = m
|
||||
self._load_from_channellogger(irc, channel, m)
|
||||
self._load_from_data(irc, channel, m)
|
||||
else:
|
||||
m = self._markovs[channel]
|
||||
return m
|
||||
|
||||
def doPrivmsg(self, irc, msg):
|
||||
(channel, message) = msg.args
|
||||
if not irc.isChannel(channel):
|
||||
return
|
||||
if not self.registryValue('enable', channel):
|
||||
return
|
||||
if self.registryValue('ignoreCommands', channel) and callbacks.addressed(irc.nick, msg):
|
||||
return
|
||||
match = False
|
||||
ignore = self.registryValue("ignorePattern", channel)
|
||||
strip = self.registryValue("stripPattern", channel)
|
||||
if ignore:
|
||||
match = re.search(ignore, message)
|
||||
if match:
|
||||
log.debug("Markovgen: %s matches ignorePattern for %s" % (message, channel))
|
||||
return
|
||||
if msg.nick.lower() in self.registryValue('ignoreNicks', channel):
|
||||
log.debug("Markovgen: nick %s in ignoreNicks for %s" % (msg.nick, channel))
|
||||
return
|
||||
m = self._get_markov(irc, channel)
|
||||
if self.registryValue('stripFormatting', channel):
|
||||
message = ircutils.stripFormatting(message)
|
||||
if strip:
|
||||
match = re.findall(strip, message)
|
||||
if match:
|
||||
for x in match:
|
||||
message = message.replace(x, '')
|
||||
message = re.sub('\s+', ' ', message)
|
||||
log.debug("Markovgen: %s matches stripPattern for %s. New message text: %s" % (x, channel, message))
|
||||
if self.registryValue('stripURL', channel):
|
||||
new_message = re.sub(r'(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))', '', message)
|
||||
new_message = re.sub('\s+', ' ', new_message)
|
||||
if new_message != message:
|
||||
log.debug("Markovgen: url(s) stripped from message for %s. New message text: %s" % (channel, new_message))
|
||||
message = new_message
|
||||
if self.registryValue('stripRelayedNick', channel):
|
||||
message = MATCH_MESSAGE_STRIPNICK.match(message).group('message')
|
||||
m.feed(message)
|
||||
tokenized_message = (w.strip(':;,.!?')
|
||||
for w in message.lower().split())
|
||||
if irc.nick.lower() in tokenized_message:
|
||||
if random.random() < self.registryValue('onNick.probability', channel):
|
||||
def replace_nick(s):
|
||||
return re.sub(re.escape(irc.nick), msg.nick, s, re.IGNORECASE)
|
||||
self._answer(irc, message, m, False,
|
||||
postprocessing=replace_nick)
|
||||
else:
|
||||
if random.random() < self.registryValue('probability', channel):
|
||||
self._answer(irc, message, m, False)
|
||||
|
||||
@wrap(['channel', optional('text')])
|
||||
def gen(self, irc, msg, args, channel, message):
|
||||
"""[<channel>] [<seed>]
|
||||
|
||||
Generates a random message based on the logs of a channel
|
||||
and a seed"""
|
||||
if not self.registryValue('enable', channel):
|
||||
irc.error(_('Markovgen is disabled for this channel.'),
|
||||
Raise=True)
|
||||
m = self._get_markov(irc, channel)
|
||||
if message:
|
||||
m.feed(message)
|
||||
self._answer(irc, message or '', m, True)
|
||||
|
||||
|
||||
def _answer(self, irc, message, m, allow_duplicate,
|
||||
postprocessing=lambda x: x):
|
||||
words = message.split()
|
||||
if len(words) == 0:
|
||||
possibilities = list(m.available_seeds())
|
||||
elif len(words) == 1:
|
||||
word = words[0]
|
||||
seeds = list(m.available_seeds())
|
||||
possibilities = [x for x in seeds if word in x]
|
||||
else:
|
||||
message_tuples = set(zip(words, words[1:]))
|
||||
if not message_tuples:
|
||||
return
|
||||
seeds = list(m.available_seeds())
|
||||
possibilities = [x for x in seeds if x in message_tuples]
|
||||
seed = list(random.choice(possibilities))
|
||||
backward_seed = list(reversed(seed))
|
||||
forward = m.generate_markov_text(seed=seed, backward=False)
|
||||
backward = m.generate_markov_text(seed=backward_seed,
|
||||
backward=True)
|
||||
try:
|
||||
answer = '%s %s' % (backward, forward.split(' ', 2)[2])
|
||||
except IndexError:
|
||||
answer = backward
|
||||
if allow_duplicate or message != answer:
|
||||
irc.reply(postprocessing(answer), prefixNick=False)
|
||||
|
||||
@wrap(['channel'])
|
||||
def doge(self, irc, msg, args, channel):
|
||||
"""takes no arguments
|
||||
|
||||
Generates a doge."""
|
||||
if not self.registryValue('enable', channel):
|
||||
irc.error(_('Markovgen is disabled for this channel.'),
|
||||
Raise=True)
|
||||
r = re.compile('^[a-zA-Zéèàù]{5,}$')
|
||||
def pred(x):
|
||||
if not r.match(x):
|
||||
return None
|
||||
else:
|
||||
return x
|
||||
m = self._get_markov(irc, channel)
|
||||
words = m.words
|
||||
words = filter(bool, map(pred, words))
|
||||
words = [x.strip(',?;.:/!') for x in m.words if pred(x)]
|
||||
w2 = random.choice(words)
|
||||
w1 = random.choice(['such', 'many', 'very'])
|
||||
irc.reply('%s %s' % (w1, w2))
|
||||
|
||||
|
||||
Class = Markovgen
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
@ -1 +0,0 @@
|
||||
markovgen
|
@ -1,37 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2014, Valentin Lorentz
|
||||
# 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.
|
||||
|
||||
###
|
||||
|
||||
from supybot.test import *
|
||||
|
||||
class MarkovgenTestCase(PluginTestCase):
|
||||
plugins = ('Markovgen',)
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
@ -1 +0,0 @@
|
||||
Fetches scores and game information from NFL.com
|
@ -1,52 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
"""
|
||||
NFLScores: Fetches scores and game information from NFL.com
|
||||
"""
|
||||
|
||||
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:
|
@ -1,33 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
from supybot import conf, registry
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('NFLScores')
|
||||
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('NFLScores', True)
|
||||
|
||||
|
||||
NFLScores = conf.registerPlugin('NFLScores')
|
||||
# This is where your configuration variables (if any) should go. For example:
|
||||
# conf.registerGlobalValue(NFLScores, 'someConfigVariableName',
|
||||
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
@ -1,518 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
import pendulum
|
||||
import requests, json
|
||||
from roman_numerals import convert_to_numeral
|
||||
|
||||
from supybot import utils, plugins, ircutils, callbacks
|
||||
from supybot.commands import *
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('NFLScores')
|
||||
except ImportError:
|
||||
# Placeholder that allows to run the plugin on a bot
|
||||
# without the i18n module
|
||||
_ = lambda x: x
|
||||
|
||||
BASE_URL = "https://feeds.nfl.com/feeds-rs{}.json"
|
||||
|
||||
def getValidDateFmt(irc, msg, args, state):
|
||||
date = args[0]
|
||||
valid = ['yesterday', 'tomorrow']
|
||||
check = None
|
||||
try:
|
||||
if date.lower() in valid:
|
||||
if date.lower() == 'yesterday':
|
||||
check = pendulum.yesterday().format('MM/DD/YYYY')
|
||||
else:
|
||||
check = pendulum.tomorrow().format('MM/DD/YYYY')
|
||||
else:
|
||||
check = pendulum.parse(date, strict=False).format('MM/DD/YYYY')
|
||||
except:
|
||||
pass
|
||||
if not check:
|
||||
state.errorInvalid(_('date format'), str(date))
|
||||
else:
|
||||
state.args.append(check)
|
||||
del args[0]
|
||||
addConverter('validDate', getValidDateFmt)
|
||||
|
||||
class NFLScores(callbacks.Plugin):
|
||||
"""Fetches scores and game information from NFL.com"""
|
||||
threaded = True
|
||||
|
||||
def __init__(self, irc):
|
||||
super().__init__(irc)
|
||||
self.GOOG = irc.getCallback('Google')
|
||||
|
||||
@wrap([getopts({"week": "positiveInt",
|
||||
"season": "positiveInt",
|
||||
"type": ("literal", ("hof","pre", "reg", "post", "pro", "sb")),
|
||||
"inp": "",
|
||||
"pro": "",
|
||||
"date": "validDate"}), optional("somethingWithoutSpaces")])
|
||||
def nfl(self, irc, msg, args, options, team):
|
||||
"""(--week <#> | --type <pre/reg/post> | --inp | --date <YYYY-MM-DD|MM/DD/YYYY>) (<team abbreviation>)
|
||||
Fetches scores.
|
||||
"""
|
||||
options = dict(options)
|
||||
inp = options.get('inp')
|
||||
seasonType = options.get('type')
|
||||
date = options.get('date') or pendulum.now().format('MM/DD/YYYY')
|
||||
week = options.get('week')
|
||||
season = options.get('season')
|
||||
gameIds = []
|
||||
network = None
|
||||
|
||||
date = dict(zip(['month', 'day', 'year'], date.split('/')))
|
||||
if 1 <= int(date['month']) <= 6:
|
||||
url = BASE_URL.format(f"/schedules/{int(date['year'])-1}")
|
||||
else:
|
||||
url = BASE_URL.format(f"/schedules/{date['year']}")
|
||||
data = requests.get(url).json()
|
||||
|
||||
if not week:
|
||||
url = BASE_URL.format('/currentWeek')
|
||||
week = requests.get(url).json()['week']
|
||||
if not season:
|
||||
url = BASE_URL.format('/currentWeek')
|
||||
season = requests.get(url).json()['seasonId']
|
||||
if not seasonType:
|
||||
url = BASE_URL.format('/currentWeek')
|
||||
tmp = requests.get(url).json()['seasonType']
|
||||
if tmp == "PRO":
|
||||
if not options.get('pro'):
|
||||
tmp = "POST"
|
||||
week = 22 if not week or week == 4 or week == 21 else week
|
||||
seasonType = tmp
|
||||
|
||||
if options.get('date'):
|
||||
found = False
|
||||
for game in data['gameSchedules']:
|
||||
if game['gameDate'] == f"{'/'.join(date.values())}":
|
||||
if team:
|
||||
teams = [game['visitorTeamAbbr'], game['homeTeamAbbr']]
|
||||
if team.upper() in teams:
|
||||
gameIds.append(game['gameId'])
|
||||
network = ' :: {}'.format(game['networkChannel'])
|
||||
week = str(game['week'])
|
||||
season = game['season']
|
||||
seasonType = game['seasonType']
|
||||
found = True
|
||||
break
|
||||
else:
|
||||
gameIds.append(game['gameId'])
|
||||
network = ' :: {}'.format(game['networkChannel'])
|
||||
week = str(game['week'])
|
||||
season = game['season']
|
||||
seasonType = game['seasonType']
|
||||
found = True
|
||||
if not found:
|
||||
date = '/'.join(date.values())
|
||||
irc.reply('Error: No games found on {}'.format(
|
||||
f"{date if not team else date + ' for ' + team.upper()}"))
|
||||
return
|
||||
|
||||
if seasonType.upper() in ['POST']:
|
||||
if int(week) <= 5: week += 17
|
||||
url = BASE_URL.format('/scores/{}/{}/{}'.format(
|
||||
season, seasonType.upper(), week
|
||||
))
|
||||
try:
|
||||
scores = requests.get(url).json()['gameScores']
|
||||
except json.decoder.JSONDecodeError:
|
||||
irc.error('invalid input', Raise=True)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
irc.error('something went wrong parsing data', Raise=True)
|
||||
|
||||
new_scores = []
|
||||
if gameIds or team:
|
||||
for game in scores:
|
||||
if gameIds and not team:
|
||||
if game['gameSchedule']['gameId'] in gameIds:
|
||||
new_scores.append(game)
|
||||
if team:
|
||||
teams = [game['gameSchedule']['visitorTeamAbbr'], game['gameSchedule']['homeTeamAbbr']]
|
||||
if team.upper() in teams:
|
||||
new_scores.append(game)
|
||||
break
|
||||
else:
|
||||
new_scores = scores
|
||||
|
||||
week = int(week)
|
||||
if week >= 18: week -= 17
|
||||
if seasonType in ['PRE']:
|
||||
if week != 0:
|
||||
prefix = self._bold("Preseason Week {}:".format(week))
|
||||
else:
|
||||
prefix = self._bold("Hall of Fame Game:")
|
||||
elif seasonType in ['REG']:
|
||||
prefix = self._bold("Week {}:".format(week))
|
||||
else:
|
||||
prefix = self._bold("{}:")
|
||||
if new_scores[0]['gameSchedule']['gameType'] == "WC":
|
||||
prefix = prefix.format("Wildcard Weekend")
|
||||
elif new_scores[0]['gameSchedule']['gameType'] == "DIV":
|
||||
prefix = prefix.format("Divisional Round")
|
||||
elif new_scores[0]['gameSchedule']['gameType'] == "CON":
|
||||
prefix = prefix.format("Conference Finals")
|
||||
elif new_scores[0]['gameSchedule']['gameType'] == "PRO":
|
||||
prefix = prefix.format("Pro Bowl")
|
||||
elif new_scores[0]['gameSchedule']['gameType'] == "SB":
|
||||
prefix = prefix.format(f"Super Bowl {convert_to_numeral(int(season)-1965)}")
|
||||
else:
|
||||
prefix = prefix.format("Week {}".format(week))
|
||||
|
||||
games = []
|
||||
print(new_scores)
|
||||
for game in new_scores:
|
||||
if len(new_scores) == 1:
|
||||
long_ = True
|
||||
home = "homeDisplayName"
|
||||
away = "visitorDisplayName"
|
||||
time_format = "dddd, M/D, h:mm A zz"
|
||||
sep = " :: "
|
||||
if not network:
|
||||
for s in data['gameSchedules']:
|
||||
if game['gameSchedule']['gameId'] == s['gameId']:
|
||||
network = ' :: {}'.format(s['networkChannel'])
|
||||
break
|
||||
else:
|
||||
long_ = False
|
||||
home = "homeTeamAbbr"
|
||||
away = "visitorTeamAbbr"
|
||||
time_format = "ddd h:mm A zz"
|
||||
sep = " "
|
||||
network = ''
|
||||
score = game['score']
|
||||
info = game['gameSchedule']
|
||||
time = f"{pendulum.from_timestamp(info['isoTime']/1000).in_tz('US/Eastern').format(time_format)}"
|
||||
if not score:
|
||||
string = (f"{info[away]} @ {info[home]}{sep}"
|
||||
f"{time}{network}")
|
||||
if info['gameType'] == "SB":
|
||||
string += f" :: {info['site']['siteFullname']}{' ({})'.format(info['site']['roofType'].title()) if info['site']['roofType'] else ''}, {info['site']['siteCity']}, {info['site']['siteState']}"
|
||||
games.append(string)
|
||||
else:
|
||||
if "FINAL" in score['phase']:
|
||||
phase = score['phase']
|
||||
if "OVERTIME" in phase:
|
||||
phase = "Final/Overtime" if long_ else "F/OT"
|
||||
else:
|
||||
phase = "Final" if long_ else "F"
|
||||
phase = self._color(phase, 'red')
|
||||
h_score = score['homeTeamScore']['pointTotal']
|
||||
v_score = score['visitorTeamScore']['pointTotal']
|
||||
if v_score > h_score:
|
||||
v_str = self._bold(f"{info[away]} {v_score}")
|
||||
h_str = f"{info[home]} {h_score}"
|
||||
elif h_score > v_score:
|
||||
v_str = f"{info[away]} {v_score}"
|
||||
h_str = self._bold(f"{info[home]} {h_score}")
|
||||
else:
|
||||
v_str = f"{info[away]} {v_score}"
|
||||
h_str = f"{info[home]} {h_score}"
|
||||
string = (f"{v_str} @ {h_str}{sep}{phase}")
|
||||
if info['gameType'] == "SB":
|
||||
string += f" :: {info['site']['siteFullname']}{' ({})'.format(info['site']['roofType'].title()) if info['site']['roofType'] else ''}, {info['site']['siteCity']}, {info['site']['siteState']}"
|
||||
games.append(string)
|
||||
elif "PRE" in score['phase']:
|
||||
string = (f"{info[away]} @ {info[home]}{sep}"
|
||||
f"{time}{network}")
|
||||
if info['gameType'] == "SB":
|
||||
string += f" :: {info['site']['siteFullname']}{' ({})'.format(info['site']['roofType'].title()) if info['site']['roofType'] else ''}, {info['site']['siteCity']}, {info['site']['siteState']}"
|
||||
games.append(string)
|
||||
elif "HALFTIME" in score['phase']:
|
||||
phase = "Halftime" if long_ else "HT"
|
||||
phase = self._color(phase, 'orange')
|
||||
h_score = score['homeTeamScore']['pointTotal']
|
||||
v_score = score['visitorTeamScore']['pointTotal']
|
||||
if v_score > h_score:
|
||||
v_str = self._bold(f"{info[away]} {v_score}")
|
||||
h_str = f"{info[home]} {h_score}"
|
||||
elif h_score > v_score:
|
||||
v_str = f"{info[away]} {v_score}"
|
||||
h_str = self._bold(f"{info[home]} {h_score}")
|
||||
else:
|
||||
v_str = f"{info[away]} {v_score}"
|
||||
h_str = f"{info[home]} {h_score}"
|
||||
string = (f"{v_str} @ {h_str}{sep}{phase}")
|
||||
games.append(string)
|
||||
else:
|
||||
phase = score['phaseDescription'] if long_ else score['phase']
|
||||
phase = self._color(phase, 'green')
|
||||
time = self._color(score['time'], 'green')
|
||||
h_score = score['homeTeamScore']['pointTotal']
|
||||
v_score = score['visitorTeamScore']['pointTotal']
|
||||
if v_score > h_score:
|
||||
v_str = self._bold(f"{info[away]} {v_score}")
|
||||
h_str = f"{info[home]} {h_score}"
|
||||
elif h_score > v_score:
|
||||
v_str = f"{info[away]} {v_score}"
|
||||
h_str = self._bold(f"{info[home]} {h_score}")
|
||||
else:
|
||||
v_str = f"{info[away]} {v_score}"
|
||||
h_str = f"{info[home]} {h_score}"
|
||||
string = (f"{v_str} @ {h_str}{sep}{time} {phase}")
|
||||
status = None
|
||||
try:
|
||||
pos_team = score.get('possessionTeamAbbr')
|
||||
at = score['yardline']
|
||||
down = "{} and {}".format(score['down'], score['yardsToGo'])
|
||||
status = " :: {}".format(down)
|
||||
last_play = None
|
||||
if pos_team:
|
||||
status += " :: {} has the ball at {}".format(pos_team, at)
|
||||
if len(new_scores) == 1:
|
||||
gameId = info['gameId']
|
||||
url = BASE_URL.format('/playbyplay/{}/latest'.format(gameId))
|
||||
try:
|
||||
last_play = requests.get(url).json()
|
||||
last_play = last_play['plays'][-1]['playDescription']
|
||||
except:
|
||||
pass
|
||||
if last_play:
|
||||
status += " :: {}".format(last_play)
|
||||
except:
|
||||
pass
|
||||
if status:
|
||||
string += status
|
||||
games.append(string)
|
||||
|
||||
irc.reply(f"{prefix} {' | '.join(games)}")
|
||||
|
||||
|
||||
|
||||
@wrap([ "text"])
|
||||
def nflgame(self, irc, msg, args, player):
|
||||
"""<player name>
|
||||
Fetches current/previous game stats for given player.
|
||||
"""
|
||||
player_id = None
|
||||
try:
|
||||
try:
|
||||
burl = "site:nfl.com {} stats".format(player.lower())
|
||||
search = self.GOOG.search('{0}'.format(burl),'#reddit-nfl',{'smallsearch': True})
|
||||
search = self.GOOG.decode(search)
|
||||
if search:
|
||||
url = search[0]['url']
|
||||
print(url)
|
||||
player_id = url.split('/')[-2]
|
||||
player_id = player_id.replace('-', ' ')
|
||||
print(player_id)
|
||||
|
||||
except:
|
||||
self.log.exception("ERROR :: NFLScores :: failed to get link for {0}".format(burl))
|
||||
pass
|
||||
except Exception as e:
|
||||
self.log.info("ERROR :: NFLScores :: {0}".format(e))
|
||||
pass
|
||||
|
||||
if not player_id:
|
||||
irc.reply('ERROR: Could not find a player id for {}'.format(player))
|
||||
return
|
||||
|
||||
endpoint = '/playerGameStats/{}'.format(player_id)
|
||||
data = requests.get(BASE_URL.format(endpoint)).json()
|
||||
game_stats = data['playerGameStats']
|
||||
player_info = data['teamPlayer']
|
||||
|
||||
if not game_stats:
|
||||
irc.reply("I couln't find any current or previous game stats for {}".format(player_info['displayName']))
|
||||
return
|
||||
|
||||
recent = game_stats[-1]
|
||||
|
||||
name = (f"{self._bold(self._color(player_info['displayName'], 'red'))}"
|
||||
f" (#{player_info['jerseyNumber']} {player_info['position']})"
|
||||
f" [{player_info['yearsOfExperience']}yrs exp]"
|
||||
f" :: {player_info['teamFullName']}")
|
||||
|
||||
game_time = recent['gameSchedule']['isoTime'] / 1000
|
||||
info = (f"{recent['gameSchedule']['visitorTeamAbbr']} "
|
||||
f"{recent['score']['visitorTeamScore']['pointTotal']} @ "
|
||||
f"{recent['gameSchedule']['homeTeamAbbr']} "
|
||||
f"{recent['score']['homeTeamScore']['pointTotal']} - "
|
||||
f"{pendulum.from_timestamp(game_time).in_tz('US/Eastern').format('ddd MM/DD h:mm A zz')}")
|
||||
|
||||
if player_info['positionGroup'] == 'QB':
|
||||
#passing, rush, fumble
|
||||
tmp = recent['playerPassingStat']
|
||||
stats = [(f"{self._ul('Passing')}: {self._bold('Comp')} {tmp.get('passingCompletions', '-')} "
|
||||
f"{self._bold('Att')} {tmp.get('passingAttempts', '-')} "
|
||||
f"{self._bold('Pct')} {tmp.get('passingCompletionPercentage', '-')} "
|
||||
f"{self._bold('Yds')} {tmp.get('passingYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('passingYardsPerAttempts', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('passingTouchdowns', '-')} "
|
||||
f"{self._bold('Int')} {tmp.get('passingInterceptions', '-')} "
|
||||
f"{self._bold('Sck')} {tmp.get('passingSacked', '-')} "
|
||||
f"{self._bold('SckY')} {tmp.get('passingSackedYardsLost', '-')} "
|
||||
f"{self._bold('Rate')} {tmp.get('passingRating', '-')}")]
|
||||
tmp = recent['playerRushingStat']
|
||||
line2 = []
|
||||
line2.append(
|
||||
(f"{self._ul('Rushing')}: {self._bold('Att')} {tmp.get('rushingAttempts', '-')} "
|
||||
f"{self._bold('Yds')} {tmp.get('rushingYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('rushingYardsPerAttempt', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('rushingTouchdowns', '-')}"))
|
||||
tmp = recent['playerFumbleStat']
|
||||
line2.append(
|
||||
(f"{self._ul('Fumbles')}: {self._bold('Fum')} {tmp.get('fumbles', '-')} "
|
||||
f"{self._bold('Lst')} {tmp.get('fumblesLost', '-')}"))
|
||||
stats.append(' :: '.join(line2))
|
||||
elif player_info['positionGroup'] == 'RB':
|
||||
#rush, recev, fumble
|
||||
line1 = []
|
||||
line2 = []
|
||||
stats = []
|
||||
tmp = recent['playerRushingStat']
|
||||
line1 = [(f"{self._ul('Rushing')}: {self._bold('Att')} {tmp.get('rushingAttempts', '-')} "
|
||||
f"{self._bold('Att')} {tmp.get('rushingYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('rushingYardsPerAttempt', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('rushingLong', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('rushingTouchdowns', '-')}")] if tmp else []
|
||||
tmp = recent['playerReceivingStat']
|
||||
if tmp: line1.append(
|
||||
(f"{self._ul('Receiving')}: {self._bold('Rec')} {tmp.get('receivingReceptions', '-')} "
|
||||
f"{self._bold('Yds')} {tmp.get('receivingYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('receivingYardsPerReception', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('receivingLong', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('receivingTouchdowns', '-')}"))
|
||||
tmp = recent['playerFumbleStat']
|
||||
line2.append(
|
||||
(f"{self._ul('Fumbles')}: {self._bold('Fum')} {tmp.get('fumbles', '-')} "
|
||||
f"{self._bold('Lst')} {tmp.get('fumblesLost', '-')}"))
|
||||
if len(line1) == 1 and len(line2) == 1:
|
||||
stats.append('{} :: {}'.format(line1[0], line2[0]))
|
||||
else:
|
||||
if line1: stats.append(' :: '.join(line1))
|
||||
if line2: stats.append(' :: '.join(line2))
|
||||
elif player_info['positionGroup'] in ['WR', 'TE']:
|
||||
#recv, rush, fumble
|
||||
line1 = []
|
||||
line2 = []
|
||||
stats = []
|
||||
tmp = recent['playerReceivingStat']
|
||||
line1 = [(f"{self._ul('Receiving')}: {self._bold('Rec')} {tmp.get('receivingReceptions', '-')} "
|
||||
f"{self._bold('Yds')} {tmp.get('receivingYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('receivingYardsPerReception', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('receivingLong', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('receivingTouchdowns', '-')}")] if tmp else []
|
||||
tmp = recent['playerRushingStat']
|
||||
if tmp: line1.append(
|
||||
(f"{self._ul('Rushing')}: {self._bold('Att')} {tmp.get('rushingAttempts', '-')} "
|
||||
f"{self._bold('Att')} {tmp.get('rushingYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('rushingYardsPerAttempt', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('rushingLong', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('rushingTouchdowns', '-')}"))
|
||||
tmp = recent['playerFumbleStat']
|
||||
line2.append(
|
||||
(f"{self._ul('Fumbles')}: {self._bold('Fum')} {tmp.get('fumbles', '-')} "
|
||||
f"{self._bold('Lst')} {tmp.get('fumblesLost', '-')}"))
|
||||
if len(line1) == 1 and len(line2) == 1:
|
||||
stats.append('{} :: {}'.format(line1[0], line2[0]))
|
||||
else:
|
||||
if line1: stats.append(' :: '.join(line1))
|
||||
if line2: stats.append(' :: '.join(line2))
|
||||
elif player_info['position'] == 'K':
|
||||
#overall fg, pats, koffs
|
||||
line1 = []
|
||||
line2 = []
|
||||
stats = []
|
||||
tmp = recent['playerKickingStat']
|
||||
line1 = [(f"{self._ul('Field Goals')}: {self._bold('FG Att')} {tmp.get('kickingFgAttempts', '-')} "
|
||||
f"{self._bold('FGM')} {tmp.get('kickingFgMade', '-')} "
|
||||
f"{self._bold('Pct')} {tmp.get('kickingFgPercentage', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('kickingFgLong', '-')} "
|
||||
f"{self._bold('Blk')} {tmp.get('kickingFgBlocked', '-')}")] if tmp else []
|
||||
if tmp: line1.append(
|
||||
(f"{self._ul('PATs')}: {self._bold('XP Att')} {tmp.get('kickingXkAttempts', '-')} "
|
||||
f"{self._bold('XPM')} {tmp.get('kickingXkMade', '-')} "
|
||||
f"{self._bold('Pct')} {tmp.get('kickingXkPercentage', '-')} "
|
||||
f"{self._bold('Blk')} {tmp.get('kickingXkBlocked', '-')} "))
|
||||
line2.append(
|
||||
(f"{self._ul('Kickoffs')}: {self._bold('KO')} {tmp.get('kickoffs', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('kickoffAverageYards', '-')} "
|
||||
f"{self._bold('TB')} {tmp.get('kickoffTouchbacks', '-')} "
|
||||
f"{self._bold('Ret')} {tmp.get('kickoffReturns', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('kickoffReturnsAverageYards', '- ')}"))
|
||||
if len(line1) == 1 and len(line2) == 1:
|
||||
stats.append('{} :: {}'.format(line1[0], line2[0]))
|
||||
else:
|
||||
if line1: stats.append(' :: '.join(line1))
|
||||
if line2: stats.append(' :: '.join(line2))
|
||||
elif player_info['positionGroup'] in ['LB', 'DB', 'DL']:
|
||||
#defense
|
||||
line1 = []
|
||||
line2 = []
|
||||
stats = []
|
||||
tmp = recent['playerDefensiveStat']
|
||||
line1 = [(f"{self._ul('Tackles')}: {self._bold('Comb')} {tmp.get('defensiveCombineTackles', '-')} "
|
||||
f"{self._bold('Total')} {tmp.get('defensiveTotalTackles', '-')} "
|
||||
f"{self._bold('Ast')} {tmp.get('defensiveAssist', '-')} "
|
||||
f"{self._bold('Sck')} {tmp.get('defensiveSacks', '-')} "
|
||||
f"{self._bold('SFTY')} {tmp.get('defensiveSafeties', '-')}")] if tmp else []
|
||||
if tmp: line1.append(
|
||||
(f"{self._ul('Interceptions')}: {self._bold('PDef')} {tmp.get('defensivePassesDefensed', '-')} "
|
||||
f"{self._bold('Int')} {tmp.get('defensiveInterceptions', '-')} "
|
||||
f"{self._bold('Yds')} {tmp.get('defensiveInterceptionYards', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('defensiveInterceptionsAvgyds', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('defensiveInterceptionsLong', '-')} "
|
||||
f"{self._bold('TDs')} {tmp.get('defensiveInterceptionsTds', '-')} "))
|
||||
line2.append(
|
||||
(f"{self._ul('Fumbles')}: {self._bold('FF')} {tmp.get('kickoffs', '-')} "))
|
||||
if len(line1) == 1 and len(line2) == 1:
|
||||
stats.append('{} :: {}'.format(line1[0], line2[0]))
|
||||
else:
|
||||
if line1: stats.append(' :: '.join(line1))
|
||||
if line2: stats.append(' :: '.join(line2))
|
||||
elif player_info['position'] == 'P':
|
||||
line1 = []
|
||||
stats = []
|
||||
tmp = recent['playerPuntingStat']
|
||||
line1 = [(f"{self._ul('Punting')}: {self._bold('Punts')} {tmp.get('puntingPunts', '-')} "
|
||||
f"{self._bold('Yds')} {tmp.get('puntingYards', '-')} "
|
||||
f"{self._bold('Net Yds')} {tmp.get('puntingNetYardage', '-')} "
|
||||
f"{self._bold('Lng')} {tmp.get('puntingLong', '-')} "
|
||||
f"{self._bold('Avg')} {tmp.get('puntingAverageYards', '-')} "
|
||||
f"{self._bold('Net Avg')} {tmp.get('puntingNetAverage', '-')} "
|
||||
f"{self._bold('Blk')} {tmp.get('puntingBlocked', '-')} "
|
||||
f"{self._bold('OOB')} {tmp.get('puntingOutOfBounds', '-')} "
|
||||
f"{self._bold('Dn')} {tmp.get('puntingDowned', '-')} "
|
||||
f"{self._bold('In 20')} {tmp.get('puntingPuntsInside20', '-')} "
|
||||
f"{self._bold('TB')} {tmp.get('puntingTouchbacks', '-')} "
|
||||
f"{self._bold('FC')} {tmp.get('puntingPuntsFairCaught', '-')} "
|
||||
f"{self._bold('Ret')} {tmp.get('puntingNumberReturned', '-')} "
|
||||
f"{self._bold('RetY')} {tmp.get('puntingReturnYards', '-')} "
|
||||
f"{self._bold('TD')} {tmp.get('puntingReturnTouchdowns', '-')}")] if tmp else []
|
||||
stats.append(' :: '.join(line1))
|
||||
else:
|
||||
stats = ["No stats found"]
|
||||
|
||||
strings = [f"{name} :: {info}"]
|
||||
|
||||
for string in strings:
|
||||
irc.reply(string)
|
||||
for stat in stats:
|
||||
irc.reply(stat)
|
||||
|
||||
def _color(self, string, color):
|
||||
return ircutils.mircColor(string, color)
|
||||
|
||||
def _bold(self, string):
|
||||
return ircutils.bold(string)
|
||||
|
||||
def _ul(self, string):
|
||||
return ircutils.underline(string)
|
||||
|
||||
|
||||
Class = NFLScores
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
@ -1,3 +0,0 @@
|
||||
pendulum
|
||||
requests
|
||||
roman_numerals
|
@ -1,15 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
from supybot.test import *
|
||||
|
||||
|
||||
class NFLScoresTestCase(PluginTestCase):
|
||||
plugins = ('NFLScores',)
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 cottongin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,42 +0,0 @@
|
||||
# TVMaze
|
||||
|
||||
## Limnoria plugin to fetch TV show information and schedules from tvmaze.com API
|
||||
|
||||
### Instructions
|
||||
|
||||
#### This plugin requires Python 3 and Limnoria
|
||||
|
||||
1. Install with PluginDownloader @install oddluck TVMaze
|
||||
|
||||
2. Install requirements for the plugin via pip
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Load the plugin on your bot
|
||||
```
|
||||
@load TVMaze
|
||||
```
|
||||
|
||||
### Example Usage
|
||||
```
|
||||
<cottongin> @schedule --country GB --tz GMT
|
||||
<bot> Today's Shows: Cuckoo: Ivy Arrives [S05E01] (10:00 AM GMT), Cuckoo: Ivy Nanny [S05E02] (10:00 AM GMT), Cuckoo: Weed Farm [S05E03] (10:00 AM GMT), Cuckoo: Macbeth [S05E04] (10:00 AM GMT), Cuckoo: Divorce Party [S05E05] (10:00 AM GMT), Cuckoo: Two Engagements and a Funeral [S05E06] (10:00 AM GMT), Cuckoo: Election [S05E07] (10:00 AM GMT), The Dumping Ground: Rage [S08E01] (5:00 PM GMT), (1 more message)
|
||||
|
||||
<cottongin> @tvshow the orville
|
||||
<bot> The Orville (2017) | Next: Home [S02E03] (2019-01-10 in 6 days) | Prev: Primal Urges [S02E02] (2019-01-03) | Running | English | 60m | FOX | Comedy/Adventure/Science-Fiction | http://www.tvmaze.com/shows/20263/the-orville | https://imdb.com/title/tt5691552/ | http://www.fox.com/the-orville
|
||||
```
|
||||
Use @help tvshow|schedule to see details on each command.
|
||||
|
||||
---
|
||||
|
||||
You can use @settvmazeoptions to save common command options to make using commands easier:
|
||||
```
|
||||
@settvmazeoptions --country GB
|
||||
@settvmazeoptions --tz US/Central
|
||||
@settvmazeoptions --country AU --tz US/Pacific
|
||||
```
|
||||
This stores settings per nick, you can clear them via --clear:
|
||||
```
|
||||
@settvmazeoptions --clear
|
||||
```
|
@ -1,54 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
"""
|
||||
TVMaze: Limnoria plugin to fetch TV show information and schedules from tvmaze.com API
|
||||
"""
|
||||
|
||||
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!
|
||||
from . import accountsdb
|
||||
reload(accountsdb)
|
||||
|
||||
if world.testing:
|
||||
from . import test
|
||||
|
||||
Class = plugin.Class
|
||||
configure = config.configure
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
@ -1,112 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, James Lu <james@overdrivenetworks.com>
|
||||
# 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.
|
||||
###
|
||||
|
||||
"""
|
||||
accountsdb: Provides storage for user-specific data via Supybot accounts, ident@host, or nicks.
|
||||
"""
|
||||
|
||||
import pickle
|
||||
|
||||
from supybot import ircdb, log, conf, registry
|
||||
|
||||
MODES = ["accounts", "identhost", "nicks"]
|
||||
DEFAULT_MODE = MODES[2]
|
||||
|
||||
class _AccountsDBAddressConfig(registry.OnlySomeStrings):
|
||||
validStrings = MODES
|
||||
|
||||
CONFIG_OPTION_NAME = "DBAddressingMode"
|
||||
CONFIG_OPTION = _AccountsDBAddressConfig(DEFAULT_MODE, """Sets the DB addressing mode.
|
||||
This requires reloading the plugin to take effect. Valid settings include accounts
|
||||
(save users by Supybot accounts and ident@host if not registered), identhost
|
||||
(save users by ident@host), and nicks (save users by nicks).
|
||||
When changing addressing modes, existing keys will be left intact, but migration between
|
||||
addressing modes is NOT supported.""")
|
||||
|
||||
class AccountsDB():
|
||||
"""
|
||||
Abstraction to map users to third-party account names.
|
||||
|
||||
This stores users by their bot account first, falling back to their
|
||||
ident@host if they are not logged in.
|
||||
"""
|
||||
|
||||
def __init__(self, plugin_name, filename, addressing_mode=DEFAULT_MODE):
|
||||
"""
|
||||
Loads the existing database, creating a new one in memory if none
|
||||
exists.
|
||||
"""
|
||||
self.db = {}
|
||||
self._plugin_name = plugin_name
|
||||
self.filename = conf.supybot.directories.data.dirize(filename)
|
||||
|
||||
self.addressing_mode = addressing_mode
|
||||
|
||||
try:
|
||||
with open(self.filename, 'rb') as f:
|
||||
self.db = pickle.load(f)
|
||||
except Exception as e:
|
||||
log.debug('%s: Unable to load database, creating '
|
||||
'a new one: %s', self._plugin_name, e)
|
||||
|
||||
def flush(self):
|
||||
"""Exports the database to a file."""
|
||||
try:
|
||||
with open(self.filename, 'wb') as f:
|
||||
pickle.dump(self.db, f, 2)
|
||||
except Exception as e:
|
||||
log.warning('%s: Unable to write database: %s', self._plugin_name, e)
|
||||
|
||||
def _get_key(self, prefix):
|
||||
nick, identhost = prefix.split('!', 1)
|
||||
|
||||
if self.addressing_mode == "accounts":
|
||||
try: # Try to first look up the caller as a bot account.
|
||||
userobj = ircdb.users.getUser(prefix)
|
||||
return userobj.name
|
||||
except KeyError: # If that fails, store them by nick@host.
|
||||
return identhost
|
||||
elif self.addressing_mode == "identhost":
|
||||
return identhost
|
||||
elif self.addressing_mode == "nicks":
|
||||
return nick
|
||||
else:
|
||||
raise ValueError("Unknown addressing mode %r" % self.addressing_mode)
|
||||
|
||||
def set(self, prefix, newId):
|
||||
"""Sets a user ID given the user's prefix."""
|
||||
user = self._get_key(prefix)
|
||||
self.db[user] = newId
|
||||
|
||||
def get(self, prefix):
|
||||
"""Sets a user ID given the user's prefix."""
|
||||
user = self._get_key(prefix)
|
||||
|
||||
# Automatically returns None if entry does not exist
|
||||
return self.db.get(user)
|
@ -1,39 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
from supybot import conf, registry
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('TVMaze')
|
||||
except:
|
||||
# Placeholder that allows to run the plugin on a bot
|
||||
# without the i18n module
|
||||
_ = lambda x: x
|
||||
|
||||
from . import accountsdb
|
||||
|
||||
|
||||
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('TVMaze', True)
|
||||
|
||||
|
||||
TVMaze = conf.registerPlugin('TVMaze')
|
||||
# This is where your configuration variables (if any) should go. For example:
|
||||
# conf.registerGlobalValue(TVMaze, 'someConfigVariableName',
|
||||
# registry.Boolean(False, _("""Help for someConfigVariableName.""")))
|
||||
conf.registerGlobalValue(TVMaze, accountsdb.CONFIG_OPTION_NAME, accountsdb.CONFIG_OPTION)
|
||||
|
||||
conf.registerChannelValue(TVMaze, 'showEpisodeTitle',
|
||||
registry.Boolean(True, _("""Determines whether the episode title will be displayed in the schedule output.""")))
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
417
TVMaze/plugin.py
417
TVMaze/plugin.py
@ -1,417 +0,0 @@
|
||||
# TVMaze v0.0.1
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
# See LICENSE.txt
|
||||
###
|
||||
|
||||
import requests
|
||||
import pendulum
|
||||
import urllib.parse
|
||||
|
||||
from . import accountsdb
|
||||
|
||||
from supybot import utils, plugins, ircutils, callbacks, world
|
||||
from supybot.commands import *
|
||||
try:
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization('TVMaze')
|
||||
except ImportError:
|
||||
# Placeholder that allows to run the plugin on a bot
|
||||
# without the i18n module
|
||||
_ = lambda x: x
|
||||
|
||||
|
||||
class TVMaze(callbacks.Plugin):
|
||||
"""Limnoria plugin to fetch TV show information and schedules from tvmaze.com API"""
|
||||
threaded = True
|
||||
|
||||
def __init__(self, irc):
|
||||
super().__init__(irc)
|
||||
self.db = accountsdb.AccountsDB("TVMaze", 'TVMaze.db', self.registryValue(accountsdb.CONFIG_OPTION_NAME))
|
||||
world.flushers.append(self.db.flush)
|
||||
|
||||
def die(self):
|
||||
world.flushers.remove(self.db.flush)
|
||||
self.db.flush()
|
||||
super().die()
|
||||
|
||||
#--------------------#
|
||||
# Formatting helpers #
|
||||
#--------------------#
|
||||
|
||||
def _bold(self, string):
|
||||
return ircutils.bold(string)
|
||||
|
||||
def _ul(self, string):
|
||||
return ircutils.underline(string)
|
||||
|
||||
def _color(self, string, color):
|
||||
return ircutils.mircColor(string, color)
|
||||
|
||||
#--------------------#
|
||||
# Internal functions #
|
||||
#--------------------#
|
||||
|
||||
def _get(self, mode, country='US', date=None, query=None, id_=None):
|
||||
"""wrapper for requests tailored to TVMaze API"""
|
||||
|
||||
base_url = 'http://api.tvmaze.com'
|
||||
|
||||
if mode == 'search':
|
||||
if not query:
|
||||
return
|
||||
query = urllib.parse.quote_plus(query)
|
||||
base_url += '/search/shows?q={}'.format(query)
|
||||
try:
|
||||
data = requests.get(base_url).json()
|
||||
except:
|
||||
data = None
|
||||
elif mode == 'schedule':
|
||||
if not date:
|
||||
date = pendulum.now().format('YYYY-MM-DD')
|
||||
base_url += '/schedule?country={}&date={}'.format(country, date)
|
||||
try:
|
||||
data = requests.get(base_url).json()
|
||||
except:
|
||||
data = None
|
||||
elif mode == 'shows':
|
||||
if not id_:
|
||||
return
|
||||
base_url += '/shows/{}?embed[]=previousepisode&embed[]=nextepisode'.format(id_)
|
||||
try:
|
||||
data = requests.get(base_url).json()
|
||||
except:
|
||||
data = None
|
||||
else:
|
||||
data = None
|
||||
|
||||
return data
|
||||
|
||||
#------------------#
|
||||
# Public functions #
|
||||
#------------------#
|
||||
|
||||
@wrap([getopts({'country': 'somethingWithoutSpaces',
|
||||
'detail': '',
|
||||
'd': '',
|
||||
'search': '',
|
||||
'record': 'positiveInt'}), 'text'])
|
||||
def tvshow(self, irc, msg, args, options, query):
|
||||
"""[--country <country> | --detail|--d] <TV Show Title>
|
||||
Fetches information about provided TV Show from TVMaze.com.
|
||||
Optionally include --country to find shows with the same name from another country.
|
||||
Optionally include --detail (or --d) to show additional details.
|
||||
Ex: tvshow --country GB the office
|
||||
"""
|
||||
# prefer manually passed options, then saved user options
|
||||
# this merges the two possible dictionaries, prefering manually passed
|
||||
# options if they already exist
|
||||
user_options = self.db.get(msg.prefix) or dict()
|
||||
options = {**user_options, **dict(options)}
|
||||
|
||||
# filter out any manually passed options
|
||||
country = options.get('country')
|
||||
show_detail = options.get('d') or options.get('detail')
|
||||
|
||||
# search for the queried TV show
|
||||
show_search = self._get('search', query=query)
|
||||
if not show_search:
|
||||
irc.reply('Nothing found for your query: {}'.format(query))
|
||||
return
|
||||
|
||||
# if the user is using --search let's just output the results
|
||||
if options.get('search'):
|
||||
results = []
|
||||
for idx, show in enumerate(show_search):
|
||||
# try to pin the year of release to the show name
|
||||
if show['show'].get('premiered'):
|
||||
premiered = show['show']['premiered'][:4]
|
||||
else:
|
||||
premiered = "TBD"
|
||||
name = "{} ({})".format(show['show']['name'], premiered)
|
||||
results.append("{}. {}".format(
|
||||
idx+1,
|
||||
self._bold(name)
|
||||
))
|
||||
irc.reply("Results: {}".format(" | ".join(results)))
|
||||
return
|
||||
|
||||
# pull a specific show from --search results
|
||||
if options.get('record'):
|
||||
if options.get('record') > len(show_search):
|
||||
irc.reply('Invalid record!')
|
||||
return
|
||||
result_to_show = options.get('record') - 1
|
||||
else:
|
||||
result_to_show = 0
|
||||
|
||||
# if we have a country, look for that first instead of the first result
|
||||
if country:
|
||||
show_id = None
|
||||
for show in show_search:
|
||||
if show['show'].get('network'):
|
||||
if show['show']['network']['country']['code'].upper() == country.upper():
|
||||
show_id = show['show']['id']
|
||||
break
|
||||
# if we can't find it, default to the first result anyway
|
||||
if not show_id:
|
||||
show_id = show_search[result_to_show]['show']['id']
|
||||
else:
|
||||
show_id = show_search[result_to_show]['show']['id']
|
||||
|
||||
# fetch the show information
|
||||
show_info = self._get('shows', id_=show_id)
|
||||
|
||||
# grab the included URLs and generate an imdb one
|
||||
urls = []
|
||||
urls.append(show_info['url'])
|
||||
urls.append('https://imdb.com/title/{}/'.format(show_info['externals']['imdb']))
|
||||
if show_info['officialSite']:
|
||||
urls.append(show_info['officialSite'])
|
||||
|
||||
# grab the genres
|
||||
genres = '{}: {}'.format(
|
||||
self._bold('Genre(s)'),
|
||||
'/'.join(show_info['genres'])
|
||||
)
|
||||
|
||||
# show name
|
||||
name = self._bold(show_info['name'])
|
||||
|
||||
# show language
|
||||
lang = "{}: {}".format(
|
||||
self._bold('Language'),
|
||||
show_info['language']
|
||||
)
|
||||
|
||||
# show status
|
||||
status = show_info['status']
|
||||
if status == 'Ended':
|
||||
status = self._color(status, 'red')
|
||||
elif status == 'Running':
|
||||
status = self._color(status, 'green')
|
||||
|
||||
# show duration
|
||||
runtime = "{}: {}m".format(
|
||||
self._bold('Duration'),
|
||||
show_info['runtime']
|
||||
)
|
||||
|
||||
# show premiere date, stripped to year and added to name
|
||||
if show_info.get('premiered'):
|
||||
premiered = show_info['premiered'][:4]
|
||||
else:
|
||||
premiered = "TBD"
|
||||
name = "{} ({})".format(name, premiered)
|
||||
|
||||
# is the show on television or web (netflix, amazon, etc)
|
||||
if show_info.get('network'):
|
||||
# we use this if --detail/--d is asked for
|
||||
network = show_info['network']['name']
|
||||
schedule = "{}: {} at {} on {}".format(
|
||||
self._bold('Schedule'),
|
||||
', '.join(show_info['schedule']['days']),
|
||||
show_info['schedule']['time'],
|
||||
network
|
||||
)
|
||||
elif show_info.get('webChannel'):
|
||||
# we use this if --detail/--d is asked for
|
||||
network = show_info['webChannel']['name']
|
||||
schedule = "Watch on {}".format(
|
||||
network
|
||||
)
|
||||
|
||||
# try to get previous and/or next episode details
|
||||
if show_info['_embedded']:
|
||||
# previous episode
|
||||
if show_info['_embedded'].get('previousepisode'):
|
||||
try:
|
||||
ep = "S{:02d}E{:02d}".format(
|
||||
show_info['_embedded']['previousepisode']['season'],
|
||||
show_info['_embedded']['previousepisode']['number']
|
||||
)
|
||||
except:
|
||||
ep = "?"
|
||||
ep = self._color(ep, 'orange')
|
||||
previous = " | {}: {ep_name} [{ep}] ({ep_date})".format(
|
||||
self._bold('Prev'),
|
||||
ep_name=show_info['_embedded']['previousepisode']['name'],
|
||||
ep=ep,
|
||||
ep_date=show_info['_embedded']['previousepisode']['airdate']
|
||||
)
|
||||
else:
|
||||
previous = ""
|
||||
# next episode
|
||||
if show_info['_embedded'].get('nextepisode'):
|
||||
try:
|
||||
ep = "S{:02d}E{:02d}".format(
|
||||
show_info['_embedded']['nextepisode']['season'],
|
||||
show_info['_embedded']['nextepisode']['number']
|
||||
)
|
||||
except:
|
||||
ep = "?"
|
||||
ep = self._color(ep, 'orange')
|
||||
next_ = " | {}: {ep_name} [{ep}] ({ep_date} {when})".format(
|
||||
self._bold('Next'),
|
||||
ep_name=show_info['_embedded']['nextepisode']['name'],
|
||||
ep=ep,
|
||||
ep_date=show_info['_embedded']['nextepisode']['airdate'],
|
||||
when=pendulum.parse(show_info['_embedded']['nextepisode']['airstamp']).diff_for_humans()
|
||||
)
|
||||
else:
|
||||
next_ = ""
|
||||
|
||||
# now finally put it all together and reply
|
||||
reply = "{0} ({3}){1}{2} | {4}".format(
|
||||
name,
|
||||
next_,
|
||||
previous,
|
||||
status,
|
||||
' | '.join(urls)
|
||||
)
|
||||
irc.reply(reply)
|
||||
|
||||
# add a second line for details if requested
|
||||
if show_detail:
|
||||
reply = "{} | {} | {} | {}".format(
|
||||
schedule,
|
||||
runtime,
|
||||
lang,
|
||||
genres
|
||||
)
|
||||
irc.reply(reply)
|
||||
|
||||
|
||||
@wrap([getopts({'all': '',
|
||||
'tz': 'somethingWithoutSpaces',
|
||||
'network': 'somethingWithoutSpaces',
|
||||
'country': 'somethingWithoutSpaces',
|
||||
'date': 'somethingWithoutSpaces',
|
||||
'showEpisodeTitle': '',
|
||||
'debug': ''})])
|
||||
def schedule(self, irc, msg, args, options):
|
||||
"""[--all | --tz <IANA timezone> | --network <network> | --country <country> | --date <YYYY-MM-DD>]
|
||||
Fetches upcoming TV schedule from TVMaze.com.
|
||||
"""
|
||||
# prefer manually passed options, then saved user options
|
||||
# this merges the two possible dictionaries, prefering manually passed
|
||||
# options if they already exist
|
||||
user_options = self.db.get(msg.prefix) or dict()
|
||||
options = {**user_options, **dict(options)}
|
||||
|
||||
# parse manually passed options, if any
|
||||
tz = options.get('tz') or 'US/Eastern'
|
||||
country = options.get('country')
|
||||
date = options.get('date')
|
||||
# TO-DO: add a --filter option(s)
|
||||
if country:
|
||||
country = country.upper()
|
||||
# if user isn't asking for a specific timezone,
|
||||
# default to some sane ones given the country
|
||||
if not options.get('tz'):
|
||||
if country == 'GB':
|
||||
tz = 'GMT'
|
||||
elif country == 'AU':
|
||||
tz = 'Australia/Sydney'
|
||||
else:
|
||||
tz = 'US/Eastern'
|
||||
else:
|
||||
country = 'US'
|
||||
# we don't need to default tz here because it's already set
|
||||
|
||||
# parse date input
|
||||
if date:
|
||||
date = pendulum.parse(date, strict=False).format('YYYY-MM-DD')
|
||||
else:
|
||||
date = pendulum.now(tz).format('YYYY-MM-DD')
|
||||
|
||||
# fetch the schedule
|
||||
schedule_data = self._get('schedule', country=country, date=date)
|
||||
|
||||
if not schedule_data:
|
||||
irc.reply('Something went wrong fetching TVMaze schedule data.')
|
||||
return
|
||||
|
||||
# parse schedule
|
||||
shows = []
|
||||
for show in schedule_data:
|
||||
tmp = "{show_name} [{ep}] ({show_time})"
|
||||
# by default we show the episode title, there is a channel config option to disable this
|
||||
# and users can override with --showEpisodeTitle flag
|
||||
show_title = options.get('showEpisodeTitle') or self.registryValue('showEpisodeTitle', msg.args[0])
|
||||
if show_title:
|
||||
name = "{1}: {0}".format(show['name'], show['show']['name'])
|
||||
else:
|
||||
name = "{0}".format(show['show']['name'])
|
||||
# try to build some season/episode information
|
||||
try:
|
||||
ep_id = "S{:02d}E{:02d}".format(show['season'], show['number'])
|
||||
except:
|
||||
ep_id = '?'
|
||||
time = pendulum.parse(show['airstamp']).in_tz(tz)
|
||||
# put it all together
|
||||
tmp = tmp.format(show_name=self._bold(name),
|
||||
ep=self._color(ep_id, 'orange'),
|
||||
show_time=time.format('h:mm A zz'))
|
||||
# depending on any options, append to list
|
||||
if options.get('all'):
|
||||
shows.append(tmp)
|
||||
elif options.get('network'):
|
||||
if show['show'].get('network'):
|
||||
if show['show']['network']['name'].lower() == options.get('network').lower():
|
||||
shows.append(tmp)
|
||||
else:
|
||||
# for now, defaults to only upcoming 'Scripted' shows
|
||||
if show['show']['type'] == 'Scripted' and pendulum.now(tz) <= time:
|
||||
shows.append(tmp)
|
||||
|
||||
# set a default message if no shows were found
|
||||
if not shows:
|
||||
shows.append('No upcoming shows found')
|
||||
|
||||
# finally reply
|
||||
reply = "{}: {}".format(self._ul("Today's Shows"), ", ".join(shows))
|
||||
if options.get('debug'):
|
||||
#irc.reply(repr(reply))
|
||||
print(repr(reply))
|
||||
irc.reply(reply)
|
||||
|
||||
|
||||
@wrap([getopts({'country': 'somethingWithoutSpaces',
|
||||
'tz': 'somethingWithoutSpaces',
|
||||
'showEpisodeTitle': 'boolean',
|
||||
'detail': 'boolean',
|
||||
'd': 'boolean',
|
||||
'clear': ''})])
|
||||
def settvmazeoptions(self, irc, msg, args, options):
|
||||
"""--country <country> | --tz <IANA timezone> | --showEpisodeTitle (True/False) | --detail/--d (True/False)
|
||||
Allows user to set options for easier use of TVMaze commands.
|
||||
Use --clear to reset all options.
|
||||
"""
|
||||
if not options:
|
||||
irc.reply('You must give me some options!')
|
||||
return
|
||||
|
||||
# prefer manually passed options, then saved user options
|
||||
# this merges the two possible dictionaries, prefering manually passed
|
||||
# options if they already exist
|
||||
user_options = self.db.get(msg.prefix) or dict()
|
||||
options = {**user_options, **dict(options)}
|
||||
|
||||
if options.get('clear'):
|
||||
self.db.set(msg.prefix, {})
|
||||
irc.replySuccess()
|
||||
return
|
||||
|
||||
self.db.set(msg.prefix, options)
|
||||
irc.replySuccess()
|
||||
|
||||
|
||||
|
||||
|
||||
Class = TVMaze
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:
|
@ -1,2 +0,0 @@
|
||||
requests
|
||||
pendulum
|
@ -1,15 +0,0 @@
|
||||
###
|
||||
# Copyright (c) 2019, cottongin
|
||||
# All rights reserved.
|
||||
#
|
||||
#
|
||||
###
|
||||
|
||||
from supybot.test import *
|
||||
|
||||
|
||||
class TVMazeTestCase(PluginTestCase):
|
||||
plugins = ('TVMaze',)
|
||||
|
||||
|
||||
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
|
Loading…
x
Reference in New Issue
Block a user