last commit

This commit is contained in:
iou1name 2018-03-16 12:08:02 -04:00
parent e013a50711
commit 7dca597a2a
9 changed files with 188 additions and 198 deletions

View File

@ -2,3 +2,5 @@ NIGGER DICKS
NIGGER DICKS NIGGER DICKS
NIGGER DICKS NIGGER DICKS
NIGGER DICKS NIGGER DICKS
Now deprecated in favor of https://git.steelbea.me/iou1name/fulvia

3
bot.py
View File

@ -1,5 +1,4 @@
#! /usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
The core bot class. Say good bye to PYthon 2. The core bot class. Say good bye to PYthon 2.
""" """

341
loader.py
View File

@ -9,220 +9,217 @@ import sys
from tools import itervalues, get_command_regexp from tools import itervalues, get_command_regexp
if sys.version_info.major >= 3:
basestring = (str, bytes)
# Can be implementation-dependent # Can be implementation-dependent
_regex_type = type(re.compile('')) _regex_type = type(re.compile(''))
def get_module_description(path): def get_module_description(path):
good_file = (os.path.isfile(path) and path.endswith('.py') good_file = (os.path.isfile(path) and path.endswith('.py')
and not path.startswith('_')) and not path.startswith('_'))
good_dir = (os.path.isdir(path) and good_dir = (os.path.isdir(path) and
os.path.isfile(os.path.join(path, '__init__.py'))) os.path.isfile(os.path.join(path, '__init__.py')))
if good_file: if good_file:
name = os.path.basename(path)[:-3] name = os.path.basename(path)[:-3]
return (name, path, imp.PY_SOURCE) return (name, path, imp.PY_SOURCE)
elif good_dir: elif good_dir:
name = os.path.basename(path) name = os.path.basename(path)
return (name, path, imp.PKG_DIRECTORY) return (name, path, imp.PKG_DIRECTORY)
else: else:
return None return None
def _update_modules_from_dir(modules, directory): def _update_modules_from_dir(modules, directory):
# Note that this modifies modules in place # Note that this modifies modules in place
for path in os.listdir(directory): for path in os.listdir(directory):
path = os.path.join(directory, path) path = os.path.join(directory, path)
result = get_module_description(path) result = get_module_description(path)
if result: if result:
modules[result[0]] = result[1:] modules[result[0]] = result[1:]
def enumerate_modules(config, show_all=False): def enumerate_modules(config, show_all=False):
"""Map the names of modules to the location of their file. """Map the names of modules to the location of their file.
Return a dict mapping the names of modules to a tuple of the module name, Return a dict mapping the names of modules to a tuple of the module name,
the pathname and either `imp.PY_SOURCE` or `imp.PKG_DIRECTORY`. This the pathname and either `imp.PY_SOURCE` or `imp.PKG_DIRECTORY`. This
searches the regular modules directory and all directories specified in the searches the regular modules directory and all directories specified in the
`core.extra` attribute of the `config` object. If two modules have the same `core.extra` attribute of the `config` object. If two modules have the same
name, the last one to be found will be returned and the rest will be name, the last one to be found will be returned and the rest will be
ignored. Modules are found starting in the regular directory, followed by ignored. Modules are found starting in the regular directory, followed by
`~/.sopel/modules`, and then through the extra directories in the order `~/.sopel/modules`, and then through the extra directories in the order
that the are specified. that the are specified.
If `show_all` is given as `True`, the `enable` and `exclude` If `show_all` is given as `True`, the `enable` and `exclude`
configuration options will be ignored, and all modules will be shown configuration options will be ignored, and all modules will be shown
(though duplicates will still be ignored as above). (though duplicates will still be ignored as above).
""" """
modules = {} modules = {}
# First, add modules from the regular modules directory # First, add modules from the regular modules directory
main_dir = os.path.dirname(os.path.abspath(__file__)) main_dir = os.path.dirname(os.path.abspath(__file__))
modules_dir = os.path.join(main_dir, 'modules') modules_dir = os.path.join(main_dir, 'modules')
_update_modules_from_dir(modules, modules_dir) _update_modules_from_dir(modules, modules_dir)
for path in os.listdir(modules_dir): for path in os.listdir(modules_dir):
break break
# Then, find PyPI installed modules # Then, find PyPI installed modules
# TODO does this work with all possible install mechanisms? # TODO does this work with all possible install mechanisms?
try: try:
import sopel_modules import sopel_modules
except: except:
pass pass
else: else:
for directory in sopel_modules.__path__: for directory in sopel_modules.__path__:
_update_modules_from_dir(modules, directory) _update_modules_from_dir(modules, directory)
# Next, look in ~/.sopel/modules # Next, look in ~/.sopel/modules
home_modules_dir = os.path.join(config.homedir, 'modules') home_modules_dir = os.path.join(config.homedir, 'modules')
if not os.path.isdir(home_modules_dir): if not os.path.isdir(home_modules_dir):
os.makedirs(home_modules_dir) os.makedirs(home_modules_dir)
_update_modules_from_dir(modules, home_modules_dir) _update_modules_from_dir(modules, home_modules_dir)
# Last, look at all the extra directories. # Last, look at all the extra directories.
for directory in config.core.extra: for directory in config.core.extra:
_update_modules_from_dir(modules, directory) _update_modules_from_dir(modules, directory)
# Coretasks is special. No custom user coretasks. # Coretasks is special. No custom user coretasks.
ct_path = os.path.join(main_dir, 'coretasks.py') ct_path = os.path.join(main_dir, 'coretasks.py')
modules['coretasks'] = (ct_path, imp.PY_SOURCE) modules['coretasks'] = (ct_path, imp.PY_SOURCE)
# If caller wants all of them, don't apply white and blacklists # If caller wants all of them, don't apply white and blacklists
if show_all: if show_all:
return modules return modules
# Apply whitelist, if present # Apply whitelist, if present
enable = config.core.enable enable = config.core.enable
if enable: if enable:
enabled_modules = {'coretasks': modules['coretasks']} enabled_modules = {'coretasks': modules['coretasks']}
for module in enable: for module in enable:
if module in modules: if module in modules:
enabled_modules[module] = modules[module] enabled_modules[module] = modules[module]
modules = enabled_modules modules = enabled_modules
# Apply blacklist, if present # Apply blacklist, if present
exclude = config.core.exclude exclude = config.core.exclude
for module in exclude: for module in exclude:
if module in modules: if module in modules:
del modules[module] del modules[module]
return modules return modules
def compile_rule(nick, pattern): def compile_rule(nick, pattern):
# Not sure why this happens on reloads, but it shouldn't cause problems… # Not sure why this happens on reloads, but it shouldn't cause problems…
if isinstance(pattern, _regex_type): if isinstance(pattern, _regex_type):
return pattern return pattern
nick = re.escape(nick) nick = re.escape(nick)
pattern = pattern.replace('$nickname', nick) pattern = pattern.replace('$nickname', nick)
pattern = pattern.replace('$nick', r'{}[,:]\s+'.format(nick)) pattern = pattern.replace('$nick', r'{}[,:]\s+'.format(nick))
flags = re.IGNORECASE flags = re.IGNORECASE
if '\n' in pattern: if '\n' in pattern:
flags |= re.VERBOSE flags |= re.VERBOSE
return re.compile(pattern, flags) return re.compile(pattern, flags)
def trim_docstring(doc): def trim_docstring(doc):
"""Get the docstring as a series of lines that can be sent""" """Get the docstring as a series of lines that can be sent"""
if not doc: if not doc:
return [] return []
lines = doc.expandtabs().splitlines() lines = doc.expandtabs().splitlines()
indent = sys.maxsize indent = sys.maxsize
for line in lines[1:]: for line in lines[1:]:
stripped = line.lstrip() stripped = line.lstrip()
if stripped: if stripped:
indent = min(indent, len(line) - len(stripped)) indent = min(indent, len(line) - len(stripped))
trimmed = [lines[0].strip()] trimmed = [lines[0].strip()]
if indent < sys.maxsize: if indent < sys.maxsize:
for line in lines[1:]: for line in lines[1:]:
trimmed.append(line[:].rstrip()) trimmed.append(line[:].rstrip())
while trimmed and not trimmed[-1]: while trimmed and not trimmed[-1]:
trimmed.pop() trimmed.pop()
while trimmed and not trimmed[0]: while trimmed and not trimmed[0]:
trimmed.pop(0) trimmed.pop(0)
return trimmed return trimmed
def clean_callable(func, config): def clean_callable(func, config):
"""Compiles the regexes, moves commands into func.rule, fixes up docs and """Compiles the regexes, moves commands into func.rule, fixes up docs and
puts them in func._docs, and sets defaults""" puts them in func._docs, and sets defaults"""
nick = config.core.nick nick = config.core.nick
prefix = config.core.prefix prefix = config.core.prefix
help_prefix = config.core.help_prefix help_prefix = config.core.help_prefix
func._docs = {} func._docs = {}
doc = trim_docstring(func.__doc__) doc = trim_docstring(func.__doc__)
example = None example = None
func.unblockable = getattr(func, 'unblockable', False) func.unblockable = getattr(func, 'unblockable', False)
func.priority = getattr(func, 'priority', 'medium') func.priority = getattr(func, 'priority', 'medium')
func.thread = getattr(func, 'thread', True) func.thread = getattr(func, 'thread', True)
func.rate = getattr(func, 'rate', 0) func.rate = getattr(func, 'rate', 0)
func.channel_rate = getattr(func, 'channel_rate', 0) func.channel_rate = getattr(func, 'channel_rate', 0)
func.global_rate = getattr(func, 'global_rate', 0) func.global_rate = getattr(func, 'global_rate', 0)
if not hasattr(func, 'event'): if not hasattr(func, 'event'):
func.event = ['PRIVMSG'] func.event = ['PRIVMSG']
else: else:
if isinstance(func.event, basestring): if isinstance(func.event, (str, bytes)):
func.event = [func.event.upper()] func.event = [func.event.upper()]
else: else:
func.event = [event.upper() for event in func.event] func.event = [event.upper() for event in func.event]
if hasattr(func, 'rule'): if hasattr(func, 'rule'):
if isinstance(func.rule, basestring): if isinstance(func.rule, (str, bytes)):
func.rule = [func.rule] func.rule = [func.rule]
func.rule = [compile_rule(nick, rule) for rule in func.rule] func.rule = [compile_rule(nick, rule) for rule in func.rule]
if hasattr(func, 'commands'): if hasattr(func, 'commands'):
func.rule = getattr(func, 'rule', []) func.rule = getattr(func, 'rule', [])
for command in func.commands: for command in func.commands:
regexp = get_command_regexp(prefix, command) regexp = get_command_regexp(prefix, command)
func.rule.append(regexp) func.rule.append(regexp)
if hasattr(func, 'example'): if hasattr(func, 'example'):
example = func.example[0]["example"] example = func.example[0]["example"]
example = example.replace('$nickname', nick) example = example.replace('$nickname', nick)
if example[0] != help_prefix and not example.startswith(nick): if example[0] != help_prefix and not example.startswith(nick):
example = help_prefix + example[len(help_prefix):] example = help_prefix + example[len(help_prefix):]
if doc or example: if doc or example:
for command in func.commands: for command in func.commands:
func._docs[command] = (doc, example) func._docs[command] = (doc, example)
def load_module(name, path, type_): def load_module(name, path, type_):
"""Load a module, and sort out the callables and shutdowns""" """Load a module, and sort out the callables and shutdowns"""
if type_ == imp.PY_SOURCE: if type_ == imp.PY_SOURCE:
with open(path) as mod: with open(path) as mod:
module = imp.load_module(name, mod, path, ('.py', 'U', type_)) module = imp.load_module(name, mod, path, ('.py', 'U', type_))
elif type_ == imp.PKG_DIRECTORY: elif type_ == imp.PKG_DIRECTORY:
module = imp.load_module(name, None, path, ('', '', type_)) module = imp.load_module(name, None, path, ('', '', type_))
else: else:
raise TypeError('Unsupported module type') raise TypeError('Unsupported module type')
return module, os.path.getmtime(path) return module, os.path.getmtime(path)
def is_triggerable(obj): def is_triggerable(obj):
return any(hasattr(obj, attr) for attr in ('rule', 'rule', 'intent', return any(hasattr(obj, attr) for attr in ('rule', 'rule', 'intent',
'commands')) 'commands'))
def clean_module(module, config): def clean_module(module, config):
callables = [] callables = []
shutdowns = [] shutdowns = []
jobs = [] jobs = []
urls = [] urls = []
for obj in itervalues(vars(module)): for key, obj in dict.items(vars(module)):
if callable(obj): if callable(obj):
if getattr(obj, '__name__', None) == 'shutdown': if getattr(obj, '__name__', None) == 'shutdown':
shutdowns.append(obj) shutdowns.append(obj)
elif is_triggerable(obj): elif is_triggerable(obj):
clean_callable(obj, config) clean_callable(obj, config)
callables.append(obj) callables.append(obj)
elif hasattr(obj, 'interval'): elif hasattr(obj, 'interval'):
clean_callable(obj, config) clean_callable(obj, config)
jobs.append(obj) jobs.append(obj)
elif hasattr(obj, 'url_regex'): elif hasattr(obj, 'url_regex'):
urls.append(obj) urls.append(obj)
return callables, jobs, shutdowns, urls return callables, jobs, shutdowns, urls

View File

@ -1,9 +1,6 @@
# coding=utf-8 #!/usr/bin/env python3
""" """
announce.py - Send a message to all channels Sends a message to all channels the bot is currently in.
Copyright © 2013, Elad Alfassa, <elad@fedoraproject.org>
Licensed under the Eiffel Forum License 2.
""" """
from module import commands, example from module import commands, example
@ -12,7 +9,7 @@ from module import commands, example
@example('.announce Some important message here') @example('.announce Some important message here')
def announce(bot, trigger): def announce(bot, trigger):
""" """
Send an announcement to all channels the bot is in Send an announcement to all channels the bot is in.
""" """
if not trigger.admin: if not trigger.admin:
bot.reply('Sorry, I can\'t let you do that') bot.reply('Sorry, I can\'t let you do that')

View File

@ -9,7 +9,6 @@ import random
from module import commands, example from module import commands, example
@commands("grog") @commands("grog")
@example(".grog")
def grog(bot, trigger): def grog(bot, trigger):
""" """
Picks a random status effect from Grog of Substantial Whimsy effect. Picks a random status effect from Grog of Substantial Whimsy effect.

View File

@ -36,7 +36,8 @@ def roomTemp(bot, trigger):
""" """
try: try:
res = requests.get("http://192.168.1.25/", timeout=10) res = requests.get("http://192.168.1.25/", timeout=10)
time.sleep(0.5) del res
time.sleep(1.5)
res = requests.get("http://192.168.1.25/", timeout=10) res = requests.get("http://192.168.1.25/", timeout=10)
except requests.exceptions.ReadTimeout: except requests.exceptions.ReadTimeout:
return bot.say("Connection error. Timeout reached.") return bot.say("Connection error. Timeout reached.")

View File

@ -15,11 +15,6 @@ import pytz
from module import commands, example, NOLIMIT from module import commands, example, NOLIMIT
from tools.time import get_timezone, format_time from tools.time import get_timezone, format_time
def filename(self):
name = self.nick + '-' + self.config.core.host + '.reminders.db'
return os.path.join(self.config.core.homedir, name)
def init_database(bot): def init_database(bot):
""" """
Initializes the 'remind' table in the bot's database. Does nothing if Initializes the 'remind' table in the bot's database. Does nothing if

View File

@ -28,8 +28,8 @@ r_bing = re.compile(r'<h3><a href="([^"]+)"')
def bing_search(query, lang='en-GB'): def bing_search(query, lang='en-GB'):
base = 'http://www.bing.com/search?mkt=%s&q=' % lang base = 'http://www.bing.com/search?mkt=%s&q=' % lang
bytes = requests.get(base + query) res = requests.get(base + query)
m = r_bing.search(bytes) m = r_bing.search(res.text)
if m: if m:
return m.group(1) return m.group(1)
@ -39,12 +39,12 @@ r_duck = re.compile(r'nofollow" class="[^"]+" href="(?!https?:\/\/r\.search\.yah
def duck_search(query): def duck_search(query):
query = query.replace('!', '') query = query.replace('!', '')
uri = 'http://duckduckgo.com/html/?q=%s&kl=uk-en' % query uri = 'http://duckduckgo.com/html/?q=%s&kl=uk-en' % query
bytes = requests.get(uri) res = requests.get(uri)
if 'requests-result' in bytes: # filter out the adds on top of the page if 'requests-result' in res.text: # filter out the adds on top of the page
bytes = bytes.split('requests-result')[1] res.text = rex.text.split('requests-result')[1]
m = r_duck.search(bytes) m = r_duck.search(res.text)
if m: if m:
return requests.decode(m.group(1)) return res.text
# Alias google_search to duck_search # Alias google_search to duck_search
google_search = duck_search google_search = duck_search

View File

@ -22,11 +22,11 @@ def setup(bot):
watching = cur.execute("SELECT * FROM watcher").fetchall() watching = cur.execute("SELECT * FROM watcher").fetchall()
except: except:
cur.execute("CREATE TABLE watcher(" cur.execute("CREATE TABLE watcher("
"api_url STRING PRIMARY KEY," "api_url TEXT PRIMARY KEY,"
"name STRING DEFAULT 'Anonymous'," "name TEXT DEFAULT 'Anonymous',"
"last_post INT," "last_post INT,"
"time_since STRING," "time_since TEXT,"
"channel STRING)") "channel TEXT)")
cur.commit() cur.commit()
else: else:
for thread in watching: for thread in watching: