Merge pull request #43 from deathbybandaid/development

Development
This commit is contained in:
Deathbybandaid 2019-06-05 16:16:14 -04:00 committed by GitHub
commit af391dbc26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
117 changed files with 3273 additions and 3357 deletions

View File

@ -7,3 +7,4 @@ recursive-include tests *
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-include sopel_modules/SpiceBot_GifSearch/gifapi *
recursive-include sopel_modules/SpiceBot_Configs/aiml *

View File

@ -0,0 +1,243 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot AI system. Based On Chatty cathy
"""
from sopel.tools import Identifier
from sopel.config.types import StaticSection, ListAttribute, ValidatedAttribute
import os
import tempfile
import aiml
from .Database import db as botdb
from .Config import config as botconfig
class SpiceBot_AI_MainSection(StaticSection):
extra = ListAttribute('extra')
gender = ValidatedAttribute('gender', default='female')
class SpiceBot_AI():
def __init__(self):
self.setup_ai()
self.braindirs = []
self.dict = {
"patterncounts": 0,
"filecounts": 0,
"sessioncache": {},
"files": {}
}
# Load AIML kernel
self.aiml_kernel = aiml.Kernel()
# aiml parser
self.aiml_parser = aiml.AimlParser.create_parser()
# Don't warn for no matches
self.aiml_kernel._verboseMode = False
# Learn responses
self.load_saved_brain()
self.load_brain()
# Load bot values
self.load_bot_values()
def setup_ai(self):
botconfig.define_section("SpiceBot_AI", SpiceBot_AI_MainSection, validate=False)
botconfig.config.aibrain = os.path.join(botconfig.homedir, botconfig.config.core.basename + '.aibrain.brn')
def load_saved_brain(self):
if os.path.isfile(botconfig.config.aibrain):
self.aiml_kernel.bootstrap(brainFile=botconfig.config.aibrain)
self.save_brain()
def load_brain(self):
import sopel_modules
braindirs = []
for plugin_dir in set(sopel_modules.__path__):
configsdir = os.path.join(plugin_dir, "SpiceBot_Configs")
aimldir = os.path.join(configsdir, "aiml")
braindirs.append(aimldir)
if len(botconfig.SpiceBot_AI.extra):
for extrabraindir in botconfig.SpiceBot_AI.extra:
braindirs.append(extrabraindir)
# learn directories
self.learn(braindirs)
self.save_brain()
def load_bot_values(self):
current_bot_db = botdb.get_bot_value('botai') or None
if current_bot_db:
for predicate in current_bot_db.keys():
predval = current_bot_db[predicate]
self.aiml_kernel.setBotPredicate(predicate, predval)
# sopel nick
self.aiml_kernel.setBotPredicate("nick", botconfig.nick)
# gender
self.aiml_kernel.setBotPredicate("gender", botconfig.SpiceBot_AI.gender.lower())
if botconfig.SpiceBot_AI.gender.lower() not in ["male", "female"]:
self.aiml_kernel.setBotPredicate("gendertype", "item")
else:
self.aiml_kernel.setBotPredicate("gendertype", botconfig.SpiceBot_AI.gender.lower())
def learn(self, braindirs):
for braindir in braindirs:
if braindir not in self.braindirs:
self.braindirs.append(braindir)
# Count matches
for pathname in os.listdir(braindir):
self.dict["filecounts"] += 1
aimlfile = os.path.join(braindir, pathname)
data = open(aimlfile).read()
count = data.count('pattern')
count = count / 2
self.dict["patterncounts"] += int(count)
tempbrain = tempfile.mkstemp()[1]
with open(tempbrain, 'w') as fileo:
fileo.write(
"<aiml version='1.0.1' encoding='UTF-8'>"
" <!-- std-startup.xml -->\n"
" <category>\n"
" <pattern>LOAD AIML B</pattern>\n"
" <template>\n"
" <learn>{}</learn>\n"
" </template>\n"
" </category>\n"
"</aiml>".format(os.path.join(braindir, "*.aiml"))
)
self.aiml_kernel.learn(tempbrain)
self.aiml_kernel.respond("LOAD AIML B")
def on_message(self, bot, trigger, message):
nick = Identifier(trigger.nick)
nick_id = botdb.get_nick_id(nick, create=True)
self.check_user_import(nick, nick_id)
message = self.bot_message_precipher(bot, trigger, message)
aiml_response = self.aiml_kernel.respond(message, nick_id)
if aiml_response:
aiml_response = self.bot_message_decipher(bot, trigger, aiml_response)
self.save_nick_session(nick, nick_id)
self.save_brain()
return aiml_response
def bot_message_precipher(self, bot, trigger, message):
# uppercase input
message = message.upper()
# punctuation
puct_dict = {"!": "exclamationmark", ".": "period", "?": "questionmark", ",": "comma"}
for puctuation in puct_dict.keys():
message = message.replace(puctuation, puct_dict[puctuation])
# bot items
for botitem in ["nick"]:
message = message.replace(str(eval("bot." + botitem)).upper(), str("bot" + botitem).upper())
for triggeritem in ["nick", "sender"]:
message = message.replace(str(eval("trigger." + triggeritem)).upper(), str("trigger" + triggeritem).upper())
return message
def bot_message_decipher(self, bot, trigger, aiml_response):
# bot items
for botitem in ["nick"]:
aiml_response = aiml_response.replace("bot" + botitem, str(eval("bot." + botitem)))
# trigger items
for triggeritem in ["nick", "sender"]:
aiml_response = aiml_response.replace("trigger" + triggeritem, str(eval("trigger." + triggeritem)))
# pronouns
botgendertype = self.aiml_kernel.getBotPredicate("gendertype")
pronounsdict = {
"male": {
"main": "he",
"possess": "his",
"self": "himself",
},
"female": {
"main": "her",
"possess": "hers",
"self": "herself",
},
"item": {
"main": "it",
"possess": "its",
"self": "itself",
},
"point": {
"main": "you",
"possess": "yours",
"self": "yourself",
},
"group": {
"main": "them",
"possess": "theirs",
"self": "themselves",
},
}
for pronounitem in pronounsdict[botgendertype].keys():
aiml_response = aiml_response.replace("BOTPRONOUN" + pronounitem, pronounsdict[botgendertype][pronounitem])
triggergendertype = self.getPredicate("gender", trigger.nick)
if not triggergendertype or triggergendertype == "":
triggergendertype = "point"
for pronounitem in pronounsdict[triggergendertype].keys():
aiml_response = aiml_response.replace("TRIGGERPRONOUN" + pronounitem, pronounsdict[triggergendertype][pronounitem])
aiml_response = "\x0315" + aiml_response + "\x03"
return aiml_response
def getPredicate(self, predicate, nick, nick_id=None):
if not nick_id:
nick = Identifier(nick)
nick_id = botdb.get_nick_id(nick, create=True)
self.aiml_kernel.getPredicate(predicate, nick_id)
def check_user_import(self, nick, nick_id=None):
if not nick_id:
nick = Identifier(nick)
nick_id = botdb.get_nick_id(nick, create=True)
if nick_id not in self.dict["sessioncache"].keys():
self.dict["sessioncache"][nick_id] = botdb.get_nick_value(nick, 'botai') or {}
for predicate in self.dict["sessioncache"][nick_id].keys():
predval = self.dict["sessioncache"][nick_id][predicate]
self.aiml_kernel.setPredicate(predicate, predval, nick_id)
# defaults
if "nick" not in self.dict["sessioncache"][nick_id].keys():
self.dict["sessioncache"][nick_id]["nick"] = nick
self.aiml_kernel.setPredicate("nick", nick, nick_id)
def save_nick_session(self, nick, nick_id=None):
if not nick_id:
nick = Identifier(nick)
nick_id = botdb.get_nick_id(nick, create=True)
sessionData = self.aiml_kernel.getSessionData(nick_id)
botdb.set_nick_value(nick, 'botai', sessionData)
def save_brain(self):
self.aiml_kernel.saveBrain(botconfig.config.aibrain)
botsessiondata = self.aiml_kernel._botPredicates
botdb.set_bot_value('botai', botsessiondata)
botai = SpiceBot_AI()

View File

@ -1,22 +1,38 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Logs system.
This Class stores logs in an easy to access manner
This is the SpiceBot Channels system.
"""
import sopel
from sopel.config.types import StaticSection, ValidatedAttribute, ListAttribute
import re
import threading
from .Config import config as botconfig
class SpiceBot_Channels_MainSection(StaticSection):
announcenew = ValidatedAttribute('announcenew', default=False)
joinall = ValidatedAttribute('joinall', default=False)
operadmin = ValidatedAttribute('operadmin', default=False)
chanignore = ListAttribute('chanignore')
class BotChannels():
"""This Logs all channels known to the server"""
def __init__(self):
self.setup_channels()
self.lock = threading.Lock()
self.channel_lock = False
self.SpiceBot_Channels = {
"list": {},
"InitialProcess": False
}
self.dict = {
"list": {},
"InitialProcess": False
}
def setup_channels(self):
botconfig.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False)
def channel_list_request(self, bot):
bot.write(['LIST'])
@ -25,16 +41,20 @@ class BotChannels():
self.channel_lock = True
def channel_list_recieve_input(self, trigger):
self.lock.acquire()
channel, _, topic = trigger.args[1:]
if channel.lower() not in self.SpiceBot_Channels['list'].keys():
self.SpiceBot_Channels['list'][channel.lower()] = dict()
self.SpiceBot_Channels['list'][channel.lower()]['name'] = channel
self.SpiceBot_Channels['list'][channel.lower()]['topic'] = self.topic_compile(topic)
if channel.lower() not in self.dict['list'].keys():
self.dict['list'][channel.lower()] = dict()
self.dict['list'][channel.lower()]['name'] = channel
self.dict['list'][channel.lower()]['topic'] = self.topic_compile(topic)
self.channel_lock = True
self.lock.release()
def channel_list_recieve_finish(self):
if not self.SpiceBot_Channels['InitialProcess']:
self.SpiceBot_Channels['InitialProcess'] = True
self.lock.acquire()
if not self.dict['InitialProcess']:
self.dict['InitialProcess'] = True
self.lock.release()
self.channel_lock = False
def topic_compile(self, topic):
@ -50,28 +70,30 @@ class BotChannels():
for channel in bot.channels.keys():
if len(bot.channels[channel].privileges.keys()) == 1 and channel not in ignorepartlist and channel.startswith("#"):
bot.part(channel, "Leaving Empty Channel")
if channel.lower() in botchannels.SpiceBot_Channels['list']:
del botchannels.SpiceBot_Channels['list'][channel.lower()]
if channel.lower() in self.dict['list']:
self.lock.acquire()
del self.dict['list'][channel.lower()]
self.lock.release()
def join_all_channels(self, bot):
if bot.config.SpiceBot_Channels.joinall:
for channel in botchannels.SpiceBot_Channels['list'].keys():
if botconfig.SpiceBot_Channels.joinall:
for channel in self.dict['list'].keys():
if channel.startswith("#"):
if channel not in bot.channels.keys() and channel not in bot.config.SpiceBot_Channels.chanignore:
bot.write(('JOIN', bot.nick, botchannels.SpiceBot_Channels['list'][channel]['name']))
if channel not in bot.channels.keys() and bot.config.SpiceBot_Channels.operadmin:
bot.write(('SAJOIN', bot.nick, botchannels.SpiceBot_Channels['list'][channel]['name']))
if channel not in bot.channels.keys() and channel not in botconfig.SpiceBot_Channels.chanignore:
bot.write(('JOIN', bot.nick, self.dict['list'][channel]['name']))
if channel not in bot.channels.keys() and botconfig.SpiceBot_Channels.operadmin:
bot.write(('SAJOIN', bot.nick, self.dict['list'][channel]['name']))
def chanadmin_all_channels(self, bot):
# Chan ADMIN +a
for channel in bot.channels.keys():
if channel.startswith("#"):
if channel not in bot.config.SpiceBot_Channels.chanignore:
if bot.config.SpiceBot_Channels.operadmin:
if channel not in botconfig.SpiceBot_Channels.chanignore:
if botconfig.SpiceBot_Channels.operadmin:
if not bot.channels[channel].privileges[bot.nick] < sopel.module.ADMIN:
bot.write(('SAMODE', channel, "+a", bot.nick))
else:
bot.part(channel)
botchannels = BotChannels()
channels = BotChannels()

View File

@ -0,0 +1,259 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Commands system.
This Class stores commands in an easy to access manner
"""
import sopel
from sopel.config.types import StaticSection, ValidatedAttribute
import os
import threading
import spicemanip
from .Logs import logs
from .Config import config as botconfig
from .Database import db as botdb
class SpiceBot_Commands_MainSection(StaticSection):
query_prefix = ValidatedAttribute('query_prefix', default="?")
class BotCommands():
"""This Logs all commands known to the bot"""
def __init__(self):
self.setup_commands()
self.lock = threading.Lock()
self.dict = {
"counts": 0,
'nickrules': [],
'nickaiml': [],
"commands": {
'module': {},
'nickname': {},
'rule': {}
},
'disabled': {}
}
self.module_files_parse()
self.nickrules()
for comtype in ['module', 'nickname', 'rule']:
logs.log('SpiceBot_Commands', "Found " + str(len(self.dict['commands'][comtype].keys())) + " " + comtype + " commands.", True)
def setup_commands(self):
botconfig.define_section("SpiceBot_Commands", SpiceBot_Commands_MainSection, validate=False)
botconfig.core.query_list = str(botconfig.SpiceBot_Commands.query_prefix).replace("\\", '').split("|")
botconfig.core.prefix_list.extend(botconfig.SpiceBot_Commands.query_prefix)
def nickrules(self):
for command in self.dict['commands']['rule'].keys():
if command.startswith("$nickname"):
command = command.split("$nickname")[-1]
if command not in self.dict['nickrules']:
self.dict['nickrules'].append(command)
def find_command_type(self, command):
for commandstype in self.dict['commands'].keys():
if commandstype not in ['rule']:
for com in self.dict['commands'][commandstype].keys():
if com.lower() == command.lower():
return commandstype
return None
def get_commands_disabled(self, channel):
if not len(list(self.dict['disabled'])):
self.dict['disabled'] = botdb.get_channel_value(channel, 'commands_disabled') or {}
return self.dict['disabled']
def check_commands_disabled(self, command, channel):
if command in self.get_commands_disabled(channel).keys():
return True
else:
return False
def get_realcom(self, command, trigger_command_type):
realcom = command
if trigger_command_type not in self.dict['commands'].keys():
return realcom
if command not in self.dict['commands'][trigger_command_type].keys():
return realcom
if "aliasfor" in self.dict['commands'][trigger_command_type][command].keys():
realcom = self.dict['commands'][trigger_command_type][command]
return realcom
def set_command_disabled(self, command, channel, timestamp, reason, bywhom):
if not len(list(self.dict['disabled'])):
self.dict['disabled'] = botdb.get_channel_value(channel, 'commands_disabled') or {}
self.dict['disabled'][command] = {"reason": reason, "timestamp": timestamp, "disabledby": bywhom}
botdb.set_channel_value(channel, 'commands_disabled', self.dict['disabled'])
def unset_command_disabled(self, command, channel):
if not len(list(self.dict['disabled'])):
self.dict['disabled'] = botdb.get_channel_value(channel, 'commands_disabled') or {}
if command in self.dict['disabled'].keys():
del self.dict['disabled'][command]
botdb.set_channel_value(channel, 'commands_disabled', self.dict['disabled'])
def register(self, command_dict):
self.lock.acquire()
command_type = command_dict["comtype"]
validcoms = command_dict["validcoms"]
if not isinstance(validcoms, list):
validcoms = [validcoms]
if command_type not in self.dict['commands'].keys():
self.dict['commands'][command_type] = dict()
dict_from_file = dict()
# default command to filename
if "validcoms" not in dict_from_file.keys():
dict_from_file["validcoms"] = validcoms
maincom = dict_from_file["validcoms"][0]
if len(dict_from_file["validcoms"]) > 1:
comaliases = spicemanip.main(dict_from_file["validcoms"], '2+', 'list')
else:
comaliases = []
self.dict['commands'][command_type][maincom] = dict_from_file
for comalias in comaliases:
if comalias not in self.dict['commands'][command_type].keys():
self.dict['commands'][command_type][comalias] = {"aliasfor": maincom}
self.lock.release()
def module_directory_list(self):
filepathlisting = []
# main Modules directory
main_dir = os.path.dirname(os.path.abspath(sopel.__file__))
modules_dir = os.path.join(main_dir, 'modules')
filepathlisting.append(modules_dir)
# Home Directory
home_modules_dir = os.path.join(botconfig.homedir, 'modules')
if os.path.isdir(home_modules_dir):
filepathlisting.append(home_modules_dir)
# pypi installed
try:
import sopel_modules
for plugin_dir in set(sopel_modules.__path__):
for pathname in os.listdir(plugin_dir):
pypi_modules_dir = os.path.join(plugin_dir, pathname)
filepathlisting.append(pypi_modules_dir)
except Exception as e:
logs.log('SpiceBOT_COMMANDS', "sopel_modules not loaded :" + str(e))
# Extra directories
for directory in botconfig.extra:
filepathlisting.append(directory)
return filepathlisting
def module_files_list(self):
filepathlisting = self.module_directory_list()
filepathlist = []
for directory in filepathlisting:
for pathname in os.listdir(directory):
path = os.path.join(directory, pathname)
if (os.path.isfile(path) and path.endswith('.py') and not path.startswith('_')):
if pathname not in ["SpiceBot_dummycommand.py"]:
filepathlist.append(str(path))
# CoreTasks
main_dir = os.path.dirname(os.path.abspath(sopel.__file__))
ct_path = os.path.join(main_dir, 'coretasks.py')
filepathlist.append(ct_path)
return filepathlist
def module_files_parse(self):
filepathlist = self.module_files_list()
for modulefile in filepathlist:
module_file_lines = []
module_file = open(modulefile, 'r')
lines = module_file.readlines()
for line in lines:
module_file_lines.append(line)
module_file.close()
detected_lines = []
for line in module_file_lines:
if str(line).startswith("@"):
line = str(line)[1:]
if str(line).startswith(tuple(["commands", "module.commands", "sopel.module.commands"])):
line = str(line).split("commands")[-1]
line = "commands" + line
detected_lines.append(line)
elif str(line).startswith(tuple(["nickname_commands", "module.nickname_commands", "sopel.module.nickname_commands"])):
line = str(line).split("nickname_commands")[-1]
line = "nickname_commands" + line
detected_lines.append(line)
elif str(line).startswith(tuple(["rule", "module.rule", "sopel.module.rule"])):
line = str(line).split("rule")[-1]
line = "rule" + line
else:
line = None
if line:
detected_lines.append(line)
if len(detected_lines):
filelinelist = []
currentsuccesslines = 0
for detected_line in detected_lines:
# Commands
if str(detected_line).startswith("commands"):
comtype = "module"
validcoms = eval(str(detected_line).split("commands")[-1])
elif str(detected_line).startswith("nickname_commands"):
comtype = "nickname"
validcoms = eval(str(detected_line).split("nickname_commands")[-1])
elif str(detected_line).startswith("rule"):
comtype = "rule"
validcoms = eval(str(detected_line).split("rule")[-1])
if isinstance(validcoms, tuple):
validcoms = list(validcoms)
else:
validcoms = [validcoms]
for regexcom in ["(.*)", '^\?(.*)']:
if regexcom in validcoms:
while regexcom in validcoms:
validcoms.remove(regexcom)
if len(validcoms):
validcomdict = {"comtype": comtype, "validcoms": validcoms}
filelinelist.append(validcomdict)
currentsuccesslines += 1
if currentsuccesslines:
self.dict['counts'] += 1
if len(filelinelist):
for atlinefound in filelinelist:
self.register(atlinefound)
commands = BotCommands()

View File

@ -0,0 +1,57 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""A way to read the bot's config without bot"""
from sopel.cli.run import build_parser, get_configuration
import sys
import os
import configparser
class BotConfig():
def __init__(self):
self.dict = {}
# Load config
parser = build_parser()
if not len(sys.argv[1:]):
argv = ['legacy']
else:
argv = sys.argv[1:]
opts = parser.parse_args(argv)
self.config = get_configuration(opts)
self.setup_config()
# load as dict
config = configparser.ConfigParser()
config.read(self.config.filename)
for each_section in config.sections():
if each_section.lower() not in self.dict.keys():
self.dict[each_section.lower()] = dict()
for (each_key, each_val) in config.items(each_section):
if each_key.lower() not in self.dict[each_section.lower()].keys():
self.dict[each_section.lower()][each_key.lower()] = each_val
def setup_config(self):
self.config.core.basename = os.path.basename(self.config.filename).rsplit('.', 1)[0]
self.config.core.prefix_list = str(self.config.core.prefix).replace("\\", '').split("|")
def define_section(self, name, cls_, validate=True):
return self.config.define_section(name, cls_, validate)
def __getattr__(self, name):
''' will only get called for undefined attributes '''
"""We will try to find a core value, or return None"""
if hasattr(self.config.core, name):
return eval("self.config.core." + name)
elif name.lower() in self.dict["core"].keys():
return self.dict["core"][str(name).lower()]
elif hasattr(self.config, name):
return eval("self.config." + name)
else:
return None
config = BotConfig()

View File

@ -0,0 +1,595 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Database
"""
# sopel imports
from sopel.tools import Identifier
import sopel.db
from sopel.db import SopelDB, _deserialize
from .Config import config as botconfig
from sqlalchemy import Column, String, ForeignKey, Integer, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.exc import SQLAlchemyError
import json
BASE = declarative_base()
class NickIDs(BASE):
"""
NickIDs SQLAlchemy Class
"""
__tablename__ = 'nick_ids'
nick_id = Column(Integer, primary_key=True)
class Nicknames(BASE):
"""
Nicknames SQLAlchemy Class
"""
__tablename__ = 'nicknames'
nick_id = Column(Integer, ForeignKey('nick_ids.nick_id'), primary_key=True)
slug = Column(String(255), primary_key=True)
canonical = Column(String(255))
class NickValues(BASE):
"""
NickValues SQLAlchemy Class
"""
__tablename__ = 'spice_nick_values'
nick_id = Column(Integer, ForeignKey('nick_ids.nick_id'), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
value = Column(Text())
class ChannelValues(BASE):
"""
ChannelValues SQLAlchemy Class
"""
__tablename__ = 'spice_channel_values'
channel = Column(String(255), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
value = Column(Text())
class PluginValues(BASE):
"""
PluginValues SQLAlchemy Class
"""
__tablename__ = 'spice_plugin_values'
plugin = Column(String(255), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
value = Column(Text())
class SpiceDB(object):
# NICK FUNCTIONS
def set_nick_value(self, nick, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the nick."""
nick = Identifier(nick)
value = json.dumps(value, ensure_ascii=False)
nick_id = self.get_nick_id(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(NickValues.nick_id == nick_id) \
.filter(NickValues.namespace == namespace)\
.filter(NickValues.key == key) \
.one_or_none()
# NickValue exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_nickvalue = NickValues(nick_id=nick_id, namespace=namespace, key=key, value=value)
session.add(new_nickvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_nick_value(self, nick, key, namespace='default'):
"""Retrieves the value for a given key associated with a nick."""
nick = Identifier(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(Nicknames.nick_id == NickValues.nick_id) \
.filter(Nicknames.slug == nick.lower()) \
.filter(NickValues.namespace == namespace)\
.filter(NickValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_nick_value(self, nick, key, namespace='default'):
"""Deletes the value for a given key to be associated with the nick."""
nick = Identifier(nick)
nick_id = self.get_nick_id(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(NickValues.nick_id == nick_id) \
.filter(NickValues.namespace == namespace)\
.filter(NickValues.key == key) \
.one_or_none()
# NickValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_nick_value(self, nick, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the nick."""
nick = Identifier(nick)
value = json.dumps(value, ensure_ascii=False)
nick_id = self.get_nick_id(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(NickValues.nick_id == nick_id) \
.filter(NickValues.namespace == namespace)\
.filter(NickValues.key == key) \
.one_or_none()
# NickValue exists, update
if result:
result.value = float(result.value) + float(value)
session.commit()
# DNE - Insert
else:
new_nickvalue = NickValues(nick_id=nick_id, namespace=namespace, key=key, value=float(value))
session.add(new_nickvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_nick_list(self, nick, key, entries, adjustmentdirection, namespace='default'):
"""Sets the value for a given key to be associated with the nick."""
nick = Identifier(nick)
if not isinstance(entries, list):
entries = [entries]
entries = json.dumps(entries, ensure_ascii=False)
nick_id = self.get_nick_id(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(NickValues.nick_id == nick_id) \
.filter(NickValues.namespace == namespace)\
.filter(NickValues.key == key) \
.one_or_none()
# NickValue exists, update
if result:
if adjustmentdirection == 'add':
for entry in entries:
if entry not in result.value:
result.value.append(entry)
elif adjustmentdirection == 'del':
for entry in entries:
while entry in result.value:
result.value.remove(entry)
session.commit()
# DNE - Insert
else:
values = []
if adjustmentdirection == 'add':
for entry in entries:
if entry not in values:
values.append(entry)
elif adjustmentdirection == 'del':
for entry in entries:
while entry in values:
values.remove(entry)
new_nickvalue = NickValues(nick_id=nick_id, namespace=namespace, key=key, value=values)
session.add(new_nickvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
# CHANNEL FUNCTIONS
def set_channel_value(self, channel, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the channel."""
channel = Identifier(channel).lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_channelvalue = ChannelValues(channel=channel, namespace=namespace, key=key, value=value)
session.add(new_channelvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_channel_value(self, channel, key, namespace='default'):
"""Retrieves the value for a given key associated with a channel."""
channel = Identifier(channel).lower()
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_channel_value(self, channel, key, namespace='default'):
"""Sets the value for a given key to be associated with the channel."""
channel = Identifier(channel).lower()
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_channel_value(self, channel, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the channel."""
channel = Identifier(channel).lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
result.value = float(result.value) + float(value)
session.commit()
# DNE - Insert
else:
new_channelvalue = ChannelValues(channel=channel, namespace=namespace, key=key, value=float(value))
session.add(new_channelvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_channel_list(self, channel, key, entries, adjustmentdirection, namespace='default'):
"""Sets the value for a given key to be associated with the channel."""
channel = Identifier(channel).lower()
if not isinstance(entries, list):
entries = [entries]
entries = json.dumps(entries, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.namespace == namespace)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
if adjustmentdirection == 'add':
for entry in entries:
if entry not in result.value:
result.value.append(entry)
elif adjustmentdirection == 'del':
for entry in entries:
while entry in result.value:
result.value.remove(entry)
session.commit()
# DNE - Insert
else:
values = []
if adjustmentdirection == 'add':
for entry in entries:
if entry not in values:
values.append(entry)
elif adjustmentdirection == 'del':
for entry in entries:
while entry in values:
values.remove(entry)
new_channelvalue = ChannelValues(channel=channel, namespace=namespace, key=key, value=values)
session.add(new_channelvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
# PLUGIN FUNCTIONS
def set_plugin_value(self, plugin, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the plugin."""
plugin = Identifier(plugin).lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.plugin == plugin)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# PluginValues exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_pluginvalue = PluginValues(plugin=plugin, namespace=namespace, key=key, value=value)
session.add(new_pluginvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_plugin_value(self, plugin, key, namespace='default'):
"""Retrieves the value for a given key associated with a plugin."""
plugin = Identifier(plugin).lower()
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.plugin == plugin)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
if result is not None:
result = result.value
return _deserialize(result)
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def delete_plugin_value(self, plugin, key, namespace='default'):
"""Deletes the value for a given key to be associated with the plugin."""
plugin = Identifier(plugin).lower()
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.plugin == plugin)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# PluginValues exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_plugin_value(self, plugin, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the plugin."""
plugin = Identifier(plugin).lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.plugin == plugin)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
result.value = float(result.value) + float(value)
session.commit()
# DNE - Insert
else:
new_pluginvalue = PluginValues(plugin=plugin, namespace=namespace, key=key, value=float(value))
session.add(new_pluginvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_plugin_list(self, plugin, key, entries, adjustmentdirection, namespace='default'):
"""Sets the value for a given key to be associated with the plugin."""
plugin = Identifier(plugin).lower()
if not isinstance(entries, list):
entries = [entries]
entries = json.dumps(entries, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(PluginValues) \
.filter(PluginValues.plugin == plugin)\
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
if adjustmentdirection == 'add':
for entry in entries:
if entry not in result.value:
result.value.append(entry)
elif adjustmentdirection == 'del':
for entry in entries:
while entry in result.value:
result.value.remove(entry)
session.commit()
# DNE - Insert
else:
values = []
if adjustmentdirection == 'add':
for entry in entries:
if entry not in values:
values.append(entry)
elif adjustmentdirection == 'del':
for entry in entries:
while entry in values:
values.remove(entry)
new_pluginvalue = PluginValues(plugin=plugin, namespace=namespace, key=key, value=values)
session.add(new_pluginvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
class BotDatabase():
"""A thread safe database cache"""
def __init__(self):
sopel.db.NickValues = NickValues
SopelDB.get_nick_value = SpiceDB.get_nick_value
SopelDB.set_nick_value = SpiceDB.set_nick_value
SopelDB.delete_nick_value = SpiceDB.delete_nick_value
SopelDB.adjust_nick_value = SpiceDB.adjust_nick_value
SopelDB.adjust_nick_list = SpiceDB.adjust_nick_list
sopel.db.ChannelValues = ChannelValues
SopelDB.get_channel_value = SpiceDB.get_channel_value
SopelDB.set_channel_value = SpiceDB.set_channel_value
SopelDB.delete_channel_value = SpiceDB.delete_channel_value
SopelDB.adjust_channel_value = SpiceDB.adjust_channel_value
SopelDB.adjust_channel_list = SpiceDB.adjust_channel_list
sopel.db.PluginValues = PluginValues
SopelDB.get_plugin_value = SpiceDB.get_plugin_value
SopelDB.set_plugin_value = SpiceDB.set_plugin_value
SopelDB.delete_plugin_value = SpiceDB.delete_plugin_value
SopelDB.adjust_plugin_value = SpiceDB.adjust_plugin_value
SopelDB.adjust_plugin_list = SpiceDB.adjust_plugin_list
self.db = SopelDB(botconfig.config)
BASE.metadata.create_all(self.db.engine)
"""Nick"""
def get_nick_id(self, nick, create=True):
return self.db.get_nick_id(nick, create)
def get_nick_value(self, nick, key, namespace='default'):
return self.db.get_nick_value(nick, key, namespace)
def set_nick_value(self, nick, key, value, namespace='default'):
return self.db.set_nick_value(nick, key, value, namespace)
def delete_nick_value(self, nick, key, namespace='default'):
return self.db.delete_nick_value(nick, key, namespace)
def adjust_nick_value(self, nick, key, value, namespace='default'):
return self.db.adjust_nick_value(nick, key, value, namespace)
def adjust_nick_list(self, nick, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust_nick_list(nick, key, entries, adjustmentdirection, namespace)
"""Bot"""
def get_bot_value(self, key, namespace='default'):
return self.db.get_nick_value(botconfig.nick, key, namespace)
def set_bot_value(self, key, value, namespace='default'):
return self.db.set_nick_value(botconfig.nick, key, value, namespace)
def delete_bot_value(self, key, namespace='default'):
return self.db.delete_nick_value(botconfig.nick, key, namespace)
def adjust_bot_value(self, key, value, namespace='default'):
return self.db.adjust_nick_value(botconfig.nick, key, value, namespace)
def adjust_bot_list(self, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust_nick_list(botconfig.nick, key, entries, adjustmentdirection, namespace)
"""Channels"""
def get_channel_value(self, channel, key, namespace='default'):
return self.db.get_channel_value(channel, key, namespace)
def set_channel_value(self, channel, key, value, namespace='default'):
return self.db.set_channel_value(channel, key, value, namespace)
def delete_channel_value(self, channel, key, namespace='default'):
return self.db.delete_channel_value(channel, key, namespace)
def adjust_channel_value(self, channel, key, value, namespace='default'):
return self.db.adjust_channel_value(channel, key, value, namespace)
def adjust_channel_list(self, nick, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust_channel_list(nick, key, entries, adjustmentdirection, namespace)
"""Plugins"""
def get_plugin_value(self, plugin, key, namespace='default'):
return self.db.get_plugin_value(plugin, key, namespace)
def set_plugin_value(self, plugin, key, value, namespace='default'):
return self.db.set_plugin_value(plugin, key, value, namespace)
def delete_plugin_value(self, plugin, key, namespace='default'):
return self.db.delete_plugin_value(plugin, key, namespace)
def adjust_plugin_value(self, plugin, key, value, namespace='default'):
return self.db.adjust_plugin_value(plugin, key, value, namespace)
def adjust_plugin_list(self, nick, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust_plugin_list(nick, key, entries, adjustmentdirection, namespace)
db = BotDatabase()

View File

@ -7,9 +7,16 @@ We utilize the Sopel code for event numbers and
self-trigger the bot into performing actions
"""
import sopel
import functools
import threading
import time
from .Logs import logs
# TODO
# full_message = ':{} PRIVMSG {} :{}'.format(hostmask, sender, msg)
class BotEvents(object):
@ -17,7 +24,7 @@ class BotEvents(object):
Events will be assigned a 4-digit number above 1000.
This allows you to do, ``@module.event(botevents.BOT_WELCOME)````
This allows you to do, ``@module.event(events.BOT_WELCOME)````
Triggers handled by this module will be processed immediately.
Others will be placed into a queue.
@ -26,26 +33,30 @@ class BotEvents(object):
"""
def __init__(self):
self.lock = threading.Lock()
self.RPL_WELCOME = '001' # This is a defined IRC event
self.BOT_UPTIME = time.time()
self.BOT_WELCOME = '1001'
self.BOT_READY = '1002'
self.BOT_CONNECTED = '1003'
self.BOT_LOADED = '1004'
self.BOT_RECONNECTED = '1005'
self.defaultevents = [self.BOT_WELCOME, self.BOT_READY, self.BOT_CONNECTED, self.BOT_LOADED, self.BOT_RECONNECTED]
self.SpiceBot_Events = {
"assigned_IDs": {1000: "Default", 1001: "BOT_WELCOME", 1002: "BOT_READY", 1003: "BOT_CONNECTED", 1004: "BOT_LOADED", 1005: "BOT_RECONNECTED"},
"triggers_recieved": {},
"trigger_queue": [],
"startup_required": [self.BOT_WELCOME, self.BOT_READY, self.BOT_CONNECTED],
"RPL_WELCOME_Count": 0
}
self.dict = {
"assigned_IDs": {1000: "BOT_UPTIME", 1001: "BOT_WELCOME", 1002: "BOT_READY", 1003: "BOT_CONNECTED", 1004: "BOT_LOADED", 1005: "BOT_RECONNECTED"},
"triggers_recieved": {},
"trigger_queue": [],
"startup_required": [self.BOT_WELCOME, self.BOT_READY, self.BOT_CONNECTED],
"RPL_WELCOME_Count": 0
}
def __getattr__(self, name):
''' will only get called for undefined attributes '''
eventnumber = max(list(self.SpiceBot_Events["assigned_IDs"].keys())) + 1
self.SpiceBot_Events["assigned_IDs"][eventnumber] = str(name)
self.lock.acquire()
eventnumber = max(list(self.dict["assigned_IDs"].keys())) + 1
self.dict["assigned_IDs"][eventnumber] = str(name)
setattr(self, name, str(eventnumber))
self.lock.release()
return str(eventnumber)
def trigger(self, bot, number, message="SpiceBot_Events"):
@ -53,7 +64,7 @@ class BotEvents(object):
if number in self.defaultevents:
self.dispatch(bot, pretriggerdict)
else:
self.SpiceBot_Events["trigger_queue"].append(pretriggerdict)
self.dict["trigger_queue"].append(pretriggerdict)
def dispatch(self, bot, pretriggerdict):
number = pretriggerdict["number"]
@ -66,43 +77,51 @@ class BotEvents(object):
self.recieved({"number": number, "message": message})
def recieved(self, trigger):
self.lock.acquire()
if isinstance(trigger, dict):
eventnumber = str(trigger["number"])
message = str(trigger["message"])
else:
eventnumber = str(trigger.event)
message = trigger.args[1]
if eventnumber not in self.SpiceBot_Events["triggers_recieved"]:
self.SpiceBot_Events["triggers_recieved"][eventnumber] = []
self.SpiceBot_Events["triggers_recieved"][eventnumber].append(message)
logs.log('SpiceBot_Events', str(eventnumber) + " " + str(message))
if eventnumber not in self.dict["triggers_recieved"]:
self.dict["triggers_recieved"][eventnumber] = []
self.dict["triggers_recieved"][eventnumber].append(message)
self.lock.release()
def check(self, checklist):
if not isinstance(checklist, list):
checklist = [str(checklist)]
for number in checklist:
if str(number) not in self.SpiceBot_Events["triggers_recieved"].keys():
if str(number) not in self.dict["triggers_recieved"].keys():
return False
return True
def startup_add(self, startlist):
self.lock.acquire()
if not isinstance(startlist, list):
startlist = [str(startlist)]
self.SpiceBot_Events["startup_required"].extend(startlist)
for eventitem in startlist:
if eventitem not in self.dict["startup_required"]:
self.dict["startup_required"].append(eventitem)
self.lock.release()
def startup_check(self):
for number in self.SpiceBot_Events["startup_required"]:
if str(number) not in self.SpiceBot_Events["triggers_recieved"].keys():
for number in self.dict["startup_required"]:
if str(number) not in self.dict["triggers_recieved"].keys():
return False
return True
def startup_debug(self):
not_done = []
for number in self.SpiceBot_Events["startup_required"]:
if str(number) not in self.SpiceBot_Events["triggers_recieved"].keys():
for number in self.dict["startup_required"]:
if str(number) not in self.dict["triggers_recieved"].keys():
not_done.append(int(number))
reference_not_done = []
for item in not_done:
reference_not_done.append(str(self.SpiceBot_Events["assigned_IDs"][item]))
reference_not_done.append(str(self.dict["assigned_IDs"][item]))
return reference_not_done
def check_ready(self, checklist):
@ -126,4 +145,4 @@ class BotEvents(object):
return actual_decorator
botevents = BotEvents()
events = BotEvents()

View File

@ -0,0 +1,37 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Update system.
"""
from sopel.config.types import StaticSection, ValidatedAttribute
class SpiceBot_Kick(StaticSection):
kick = ValidatedAttribute('kick', default=1)
class SopelKICK:
def kick(self, nick, channel, text=None):
"""Send an IRC KICK command.
Within the context of a triggered callable, ``channel`` will default to the
channel in which the call was triggered. If triggered from a private message,
``channel`` is required (or the call to ``kick()`` will be ignored).
The bot must be a channel operator in specified channel for this to work.
.. versionadded:: 7.0
"""
self.write(['KICK', channel, nick], text)
class SopelWrapperKICK(object):
def kick(self, nick, channel=None, message=None):
if channel is None:
if self._trigger.is_privmsg:
raise RuntimeError('Error: KICK requires a channel.')
else:
channel = self._trigger.sender
if nick is None:
raise RuntimeError('Error: KICK requires a nick.')
self._bot.kick(nick, channel, message)

View File

@ -6,51 +6,74 @@ This is the SpiceBot Logs system.
This Class stores logs in an easy to access manner
"""
import sopel
import threading
import os
import sys
import spicemanip
from .Config import config as botconfig
class botlogs():
# TODO timestamps
class BotLogs():
"""This Is a contained source of log information"""
def __init__(self):
self.SpiceBot_Logs = {
"list": {"Sopel_systemd": [], "Sopel_stdio": []},
"queue": []
}
self.sopel_config = {
"logging_channel": True
}
self.setup_logs()
self.lock = threading.Lock()
self.dict = {
"list": {"Sopel_systemd": [], "Sopel_stdio": []},
"queue": []
}
def setup_logs(self):
botconfig.core.logs_stdio = os.path.join(botconfig.core.logdir, 'stdio.log')
# botconfig.core.logs_stdio = os.path.os.path.join(botconfig.core.logdir, botconfig.basename + '.stdio.log')
botconfig.core.logs_exceptions = os.path.join(botconfig.core.logdir, 'exceptions.log')
# botconfig.core.logs_exceptions = os.path.os.path.join(botconfig.core.logdir, botconfig.basename + '.exceptions.log')
botconfig.core.logs_raw = os.path.join(botconfig.core.logdir, 'raw.log')
# botconfig.core.logs_raw = os.path.os.path.join(botconfig.core.logdir, botconfig.basename + '.raw.log')
def botstderr(self, logmessage):
sys.stderr.write(logmessage)
def log(self, logtype, logentry, stdio=False):
self.lock.acquire()
logtitle = "[" + logtype + "]"
logmessage = logtitle + " " + logentry
if self.sopel_config["logging_channel"]:
self.SpiceBot_Logs["queue"].append(logmessage)
if botconfig.logging_channel:
self.dict["queue"].append(logmessage)
if stdio:
sopel.tools.stderr(logmessage)
self.botstderr(logmessage)
if logtype not in self.SpiceBot_Logs["list"].keys():
self.SpiceBot_Logs["list"][logtype] = []
self.SpiceBot_Logs["list"][logtype].append(logentry)
if logtype not in self.dict["list"].keys():
self.dict["list"][logtype] = []
self.dict["list"][logtype].append(logentry)
def get_logs(self):
return []
self.lock.release()
def stdio_logs_fetch(self, bot):
def get_logs(self, logtype):
if logtype == "Sopel_systemd":
logindex = self.systemd_logs_fetch()
elif logtype == "Sopel_stdio":
logindex = self.stdio_logs_fetch()
else:
logindex = self.dict["list"][logtype]
return logindex
def stdio_logs_fetch(self):
stdio_ignore = []
for logtype in botlogs.SpiceBot_Logs["list"].keys():
for logtype in self.dict["list"].keys():
stdio_ignore.append("[" + logtype + "]")
logfile = os.path.os.path.join(bot.config.core.logdir, 'stdio.log')
try:
log_file_lines = []
log_file = open(logfile, 'r')
log_file = open(botconfig.logs_stdio, 'r')
lines = log_file.readlines()
for line in lines:
log_file_lines.append(line)
@ -80,8 +103,8 @@ class botlogs():
return debuglines
def systemd_logs_fetch(self, bot):
servicepid = self.get_running_pid(bot)
def systemd_logs_fetch(self):
servicepid = self.get_running_pid()
debuglines = []
for line in os.popen(str("sudo journalctl _PID=" + str(servicepid))).read().split('\n'):
line = str(line).split(str(os.uname()[1] + " "))[-1]
@ -92,15 +115,15 @@ class botlogs():
debuglines.append(str(line))
return debuglines
def get_running_pid(self, bot):
def get_running_pid(self):
try:
filename = "/run/sopel/sopel-" + str(bot.nick) + ".pid"
filename = "/run/sopel/sopel-" + str(botconfig.nick) + ".pid"
with open(filename, 'r') as pid_file:
pidnum = int(pid_file.read())
except Exception as e:
pidnum = e
pidnum = str(os.popen("systemctl show " + str(bot.nick) + " --property=MainPID").read()).split("=")[-1]
pidnum = str(os.popen("systemctl show " + str(botconfig.nick) + " --property=MainPID").read()).split("=")[-1]
return pidnum
botlogs = botlogs()
logs = BotLogs()

View File

@ -0,0 +1,198 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Prerun system.
"""
from sopel.tools import Identifier
import functools
import copy
import datetime
import spicemanip
from .Tools import command_permissions_check
from .Commands import commands as botcommands
from .Database import db as botdb
class BotPrerun():
def __init__(self):
self.dict = {}
"""Command Splitting"""
def prerun(self, trigger_command_type='module'):
def actual_decorator(function):
@functools.wraps(function)
def internal_prerun(bot, trigger, *args, **kwargs):
# Primary command used for trigger, and a list of all words
trigger_args, trigger_command = self.trigger_args(trigger.args[1], trigger_command_type)
# Argsdict Defaults
argsdict_default = {}
argsdict_default["type"] = trigger_command_type
argsdict_default["com"] = trigger_command
realcom = botcommands.get_realcom(argsdict_default["com"], trigger_command_type)
argsdict_default["realcom"] = realcom
# split into && groupings
and_split = self.trigger_and_split(trigger_args)
# Create dict listings for trigger.sb
argsdict_list = self.trigger_argsdict_list(argsdict_default, and_split)
# Run the function for all splits
for argsdict in argsdict_list:
trigger.sb = copy.deepcopy(argsdict)
trigger.sb["args"], trigger.sb["hyphen_arg"] = self.trigger_hyphen_args(trigger.sb["args"])
if not trigger.sb["hyphen_arg"]:
# check if anything prohibits the nick from running the command
if self.trigger_runstatus(bot, trigger):
function(bot, trigger, *args, **kwargs)
else:
self.trigger_hyphen_arg_handler(bot, trigger)
return
return internal_prerun
return actual_decorator
def trigger_args(self, triggerargs_one, trigger_command_type='module'):
trigger_args = spicemanip.main(triggerargs_one, 'create')
if trigger_command_type in ['nickname']:
trigger_command = spicemanip.main(trigger_args, 2).lower()
trigger_args = spicemanip.main(trigger_args, '3+', 'list')
else:
trigger_command = spicemanip.main(trigger_args, 1).lower()[1:]
trigger_args = spicemanip.main(trigger_args, '2+', 'list')
return trigger_args, trigger_command
def trigger_and_split(self, trigger_args):
trigger_args_list_split = spicemanip.main(trigger_args, "split_&&")
if not len(trigger_args_list_split):
trigger_args_list_split.append([])
return trigger_args_list_split
def trigger_argsdict_list(self, argsdict_default, and_split):
prerun_split = []
for trigger_args_part in and_split:
argsdict_part = copy.deepcopy(argsdict_default)
argsdict_part["args"] = spicemanip.main(trigger_args_part, 'create')
if len(argsdict_part["args"]) and argsdict_part["args"][0] == "-a":
argsdict_part["adminswitch"] = True
argsdict_part["args"] = spicemanip.main(argsdict_part["args"], '2+', 'list')
else:
argsdict_part["adminswitch"] = False
prerun_split.append(argsdict_part)
return prerun_split
def trigger_runstatus(self, bot, trigger):
return True
# Bots can't run commands
if Identifier(trigger.nick) == bot.nick:
return False
# Allow permissions for enabling and disabling commands via hyphenargs
if trigger.sb["adminswitch"]:
if command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
return True
else:
bot.osd("The admin switch (-a) is for use by authorized nicks ONLY.", 'notice')
return False
# don't run commands that are disabled in channels
if not trigger.is_privmsg:
commandused = trigger.sb["realcom"]
disabled_list = botdb.get_channel_value(trigger.sender, 'commands_disabled') or {}
if commandused in disabled_list.keys():
reason = disabled_list[trigger.sb["realcom"]]["reason"]
timestamp = disabled_list[trigger.sb["realcom"]]["timestamp"]
bywhom = disabled_list[trigger.sb["realcom"]]["disabledby"]
message = "The " + str(trigger.sb["com"]) + " command was disabled by " + bywhom + " in " + str(trigger.sender) + " at " + str(timestamp) + " for the following reason: " + str(reason)
return self.trigger_cant_run(bot, trigger, message)
return True
def trigger_cant_run(self, bot, trigger, message=None):
if message:
bot.osd(message, trigger.nick, 'notice')
if command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
bot.osd("You however are authorized to bypass this warning with the (-a) admin switch.", trigger.nick, 'notice')
return False
def trigger_hyphen_args(self, trigger_args_part):
valid_hyphen_args = [
'check',
'enable', 'disable'
]
hyphen_args = []
trigger_args_unhyphend = []
for worditem in trigger_args_part:
if str(worditem).startswith("--") and worditem[2:] in valid_hyphen_args:
hyphen_args.append(worditem[2:])
else:
trigger_args_unhyphend.append(worditem)
if len(hyphen_args):
hyphenarg = hyphen_args[0]
else:
hyphenarg = None
return trigger_args_unhyphend, hyphenarg
def trigger_hyphen_arg_handler(self, bot, trigger):
# Commands that cannot run via privmsg
if trigger.sb["hyphen_arg"] in ['check']:
if trigger.sb["com"].lower() != trigger.sb["realcom"]:
bot.osd(trigger.sb["com"] + " is a valid alias command for " + trigger.sb["realcom"])
else:
bot.osd(trigger.sb["com"] + " is a valid command")
return
elif trigger.sb["hyphen_arg"] in ['enable']:
if not command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
bot.osd("I was unable to disable this command due to privilege issues.")
return
if trigger.is_privmsg:
bot.osd("This command must be run in a channel you which to enable it in.", trigger.nick, 'notice')
return
if not botcommands.check_commands_disabled(trigger.sb["realcom"], trigger.sender):
bot.osd(trigger.sb["com"] + " is already enabled in " + str(trigger.sender), trigger.nick, 'notice')
return
botcommands.unset_command_disabled(trigger.sb["realcom"], trigger.sender)
bot.osd(trigger.sb["com"] + " is now enabled in " + str(trigger.sender))
return
elif trigger.sb["hyphen_arg"] in ['disable']:
if not command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
bot.osd("I was unable to disable this command due to privilege issues.")
return
if trigger.is_privmsg:
bot.osd("This command must be run in a channel you which to disable it in.", trigger.nick, 'notice')
return
if botcommands.check_commands_disabled(trigger.sb["realcom"], trigger.sender):
bot.osd(trigger.sb["com"] + " is already disabled in " + str(trigger.sender), trigger.nick, 'notice')
return
trailingmessage = spicemanip.main(trigger.sb["args"], 0) or "No reason given."
timestamp = str(datetime.datetime.utcnow())
botcommands.set_command_disabled(trigger.sb["realcom"], trigger.sender, timestamp, trailingmessage, trigger.nick)
bot.osd(trigger.sb["com"] + " is now disabled in " + str(trigger.sender))
return
return
prerun = BotPrerun()

View File

@ -0,0 +1,13 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""StartupMonologue class"""
class BotMonologue():
def __init__(self):
self.dict = {}
startupmonologue = BotMonologue()

View File

@ -7,7 +7,6 @@ from __future__ import unicode_literals, absolute_import, print_function, divisi
import sopel
import collections
import re
import os
import sys
import codecs
@ -20,7 +19,8 @@ from pygit2 import clone_repository
import spicemanip
from sopel_modules.SpiceBot.Logs import botlogs
from .Logs import logs
from .Config import config as botconfig
"""Variable References"""
@ -39,27 +39,10 @@ github_dict = {
"""Sopel Wrapping Tools"""
def sopel_triggerargs(bot, trigger, command_type='module_command'):
triggerargs = []
if len(trigger.args) > 1:
triggerargs = spicemanip.main(trigger.args[1], 'create')
triggerargs = spicemanip.main(triggerargs, 'create')
if command_type in ['nickname_command']:
command = spicemanip.main(triggerargs, 2).lower()
triggerargs = spicemanip.main(triggerargs, '3+', 'list')
else:
command = spicemanip.main(triggerargs, 1).lower()[1:]
triggerargs = spicemanip.main(triggerargs, '2+', 'list')
return triggerargs, command
def bot_privs(bot, privtype):
def bot_privs(privtype):
if privtype == 'owners':
privtype = 'owner'
botpriveval = eval("bot.config.core." + privtype)
botpriveval = eval("botconfig." + privtype)
if not isinstance(botpriveval, list):
botpriveval = [botpriveval]
return botpriveval
@ -71,8 +54,8 @@ def command_permissions_check(bot, trigger, privslist):
for botpriv in ["admins", "owner"]:
if botpriv in privslist:
botpriveval = bot_privs(bot, botpriv)
if not inlist(bot, trigger.nick, botpriveval):
botpriveval = bot_privs(botpriv)
if not inlist(trigger.nick, botpriveval):
commandrunconsensus.append('False')
else:
commandrunconsensus.append('True')
@ -81,7 +64,7 @@ def command_permissions_check(bot, trigger, privslist):
for chanpriv in ['OP', 'HOP', 'VOICE', 'OWNER', 'ADMIN']:
if chanpriv in privslist:
chanpriveval = channel_privs(bot, trigger.sender, chanpriv)
if not inlist(bot, trigger.nick, chanpriveval):
if not inlist(trigger.nick, chanpriveval):
commandrunconsensus.append('False')
else:
commandrunconsensus.append('True')
@ -135,7 +118,7 @@ def humanized_time(countdownseconds):
"""Online Information Requests"""
def googlesearch(bot, searchterm, searchtype=None):
def googlesearch(searchterm, searchtype=None):
header = {'User-Agent': str(UserAgent().chrome)}
data = searchterm.replace(' ', '+')
lookfor = data.replace(':', '%3A')
@ -157,7 +140,7 @@ def googlesearch(bot, searchterm, searchtype=None):
"""List Manipulation Functions"""
def inlist(bot, searchterm, searchlist):
def inlist(searchterm, searchlist):
# verify we are searching a list
if isinstance(searchlist, collections.abc.KeysView) or isinstance(searchlist, dict):
@ -178,7 +161,7 @@ def inlist(bot, searchterm, searchlist):
return False
def inlist_match(bot, searchterm, searchlist):
def inlist_match(searchterm, searchlist):
# verify we are searching a list
if isinstance(searchlist, collections.abc.KeysView) or isinstance(searchlist, dict):
searchlist = [x for x in searchlist]
@ -202,7 +185,7 @@ def similar(a, b):
return SequenceMatcher(None, a, b).ratio()
def similar_list(bot, searchitem, searchlist, matchcount=1, searchorder='reverse'):
def similar_list(searchitem, searchlist, matchcount=1, searchorder='reverse'):
if sys.version_info.major >= 3:
if isinstance(searchlist, abc.KeysView):
@ -231,7 +214,7 @@ def similar_list(bot, searchitem, searchlist, matchcount=1, searchorder='reverse
return sim_listitems
def array_arrangesort(bot, sortbyarray, arrayb):
def array_arrangesort(sortbyarray, arrayb):
sortbyarray, arrayb = (list(x) for x in zip(*sorted(zip(sortbyarray, arrayb), key=itemgetter(0))))
return sortbyarray, arrayb
@ -246,12 +229,6 @@ def letters_in_string(text):
"""Channel Functions"""
def topic_compile(topic):
actual_topic = re.compile(r'^\[\+[a-zA-Z]+\] (.*)')
topic = re.sub(actual_topic, r'\1', topic)
return topic
def channel_privs(bot, channel, privtype):
privlist = []
for user in bot.channels[channel].privileges.keys():
@ -273,7 +250,7 @@ def channel_privs(bot, channel, privtype):
"""Environment Functions"""
def stock_modules_begone(bot):
def stock_modules_begone():
# Remove stock modules, if present
main_sopel_dir = os.path.dirname(os.path.abspath(sopel.__file__))
@ -297,7 +274,7 @@ def stock_modules_begone(bot):
os.system("sudo mv " + path + " " + stockdir)
def spicebot_update(bot, deps=False):
def spicebot_update(deps=False):
if not os.path.exists("/tmp") or not os.path.isdir("/tmp"):
os.system("sudo mkdir /tmp")
@ -305,48 +282,48 @@ def spicebot_update(bot, deps=False):
if not os.path.exists(clonepath) or not os.path.isdir(clonepath):
os.system(clonepath)
botlogs.log('SpiceBot_Update', "Cloning to " + clonepath, True)
logs.log('SpiceBot_Update', "Cloning to " + clonepath, True)
clone_repository(str(bot.config.SpiceBot_Update.gitrepo + ".git"), clonepath, checkout_branch=bot.config.SpiceBot_Update.gitbranch)
clone_repository(str(botconfig.SpiceBot_Update.gitrepo + ".git"), clonepath, checkout_branch=botconfig.SpiceBot_Update.gitbranch)
pipcommand = "sudo pip3 install --upgrade"
if not deps:
pipcommand += " --no-deps"
pipcommand += " --force-reinstall"
# pipcommand += " git+" + str(bot.config.SpiceBot_Update.gitrepo) + "@" + str(bot.config.SpiceBot_Update.gitbranch)
# pipcommand += " git+" + str(botconfig.SpiceBot_Update.gitrepo) + "@" + str(botconfig.SpiceBot_Update.gitbranch)
pipcommand += " /tmp/SpiceBot/"
botlogs.log('SpiceBot_Update', "Running `" + pipcommand + "`", True)
logs.log('SpiceBot_Update', "Running `" + pipcommand + "`", True)
# for line in os.popen(pipcommand).read().split('\n'):
# botlogs.log('SpiceBot_Update', " " + line)
# logs.log('SpiceBot_Update', " " + line)
os.system(pipcommand)
botlogs.log('SpiceBot_Update', "Deleting " + clonepath, True)
logs.log('SpiceBot_Update', "Deleting " + clonepath, True)
os.system("sudo rm -r /tmp/SpiceBot")
stock_modules_begone(bot)
stock_modules_begone()
def spicebot_reload(bot, log_from='service_manip', quitmessage='Recieved QUIT'):
# service_manip(bot, bot.nick, 'restart', log_from)
# service_manip(bot.nick, 'restart', log_from)
bot.restart(quitmessage)
def service_manip(bot, servicename, dowhat, log_from='service_manip'):
def service_manip(servicename, dowhat, log_from='service_manip'):
if str(dowhat) not in ["start", "stop", "restart"]:
return
try:
botlogs.log(log_from, str(dowhat).title() + "ing " + str(servicename) + ".service.")
logs.log(log_from, str(dowhat).title() + "ing " + str(servicename) + ".service.")
os.system("sudo service " + str(servicename) + " " + str(dowhat))
except Exception as e:
botlogs.log(log_from, str(dowhat).title() + "ing " + str(servicename) + ".service Failed: " + str(e))
logs.log(log_from, str(dowhat).title() + "ing " + str(servicename) + ".service Failed: " + str(e))
"""Config Reading Functions"""
def read_directory_json_to_dict(bot, directories, configtypename="Config File", log_from='read_directory_json_to_dict'):
def read_directory_json_to_dict(directories, configtypename="Config File", log_from='read_directory_json_to_dict', logging=True):
if not isinstance(directories, list):
directories = [directories]
@ -371,8 +348,8 @@ def read_directory_json_to_dict(bot, directories, configtypename="Config File",
dict_from_file = eval(infread)
except Exception as e:
filereadgood = False
if bot:
botlogs.log(log_from, "Error loading %s: %s (%s)" % (configtypename, e, filepath))
if logging:
logs.log(log_from, "Error loading %s: %s (%s)" % (configtypename, e, filepath))
dict_from_file = dict()
# Close File
inf.close()
@ -386,11 +363,11 @@ def read_directory_json_to_dict(bot, directories, configtypename="Config File",
fileopenfail += 1
if filecount:
if bot:
botlogs.log(log_from, 'Registered %d %s dict files,' % (filecount, configtypename))
botlogs.log(log_from, '%d %s dict files failed to load' % (fileopenfail, configtypename), True)
if logging:
logs.log(log_from, 'Registered %d %s dict files,' % (filecount, configtypename))
logs.log(log_from, '%d %s dict files failed to load' % (fileopenfail, configtypename), True)
else:
if bot:
botlogs.log(log_from, "Warning: Couldn't load any %s dict files" % (configtypename))
if logging:
logs.log(log_from, "Warning: Couldn't load any %s dict files" % (configtypename))
return configs_dict

View File

@ -0,0 +1,12 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Update system.
"""
from sopel.config.types import StaticSection, ValidatedAttribute
class SpiceBot_Update_MainSection(StaticSection):
gitrepo = ValidatedAttribute('gitrepo', default="https://github.com/SpiceBot/SpiceBot")
gitbranch = ValidatedAttribute('gitbranch', default="master")

View File

@ -6,9 +6,26 @@ These are the core SpiceBot Classes
This module contains references only for other modules to utilize.
"""
from .Config import *
from .Logs import *
from .Database import *
from .Update import *
from .osd import *
from .Kick import *
from .Tools import *
from .Prerun import *
from .Commands import *
from .Events import *
from .StartupMonologue import *
from .Channels import *
from .AI import *
from .configure import *
from .setup import *
from .shutdown import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'

View File

@ -0,0 +1,38 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""Configuration for SpiceBot"""
from .Channels import SpiceBot_Channels_MainSection
from .AI import SpiceBot_AI_MainSection
from .Update import SpiceBot_Update_MainSection
from .Kick import SpiceBot_Kick
from .osd import SpiceBot_OSD
def configure(config):
config.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False)
config.SpiceBot_OSD.configure_setting('notice', 'MAXTARG limit for NOTICE')
config.SpiceBot_OSD.configure_setting('privmsg', 'MAXTARG limit for PRIVMSG')
config.SpiceBot_OSD.configure_setting('flood_burst_lines', 'How many messages can be sent in burst mode.')
config.SpiceBot_OSD.configure_setting('flood_empty_wait', 'How long to wait between sending messages when not in burst mode, in seconds.')
config.SpiceBot_OSD.configure_setting('flood_refill_rate', 'How quickly burst mode recovers, in messages per second.')
config.SpiceBot_OSD.configure_setting('flood_throttle', 'Whether messages will be throttled if too many are sent in a short time.')
config.SpiceBot_OSD.configure_setting('flood_dots', "Whether repeated messages will be replaced with '...', then dropped.")
config.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False)
config.SpiceBot_Channels.configure_setting('announcenew', 'SpiceBot_Channels Announce New Channels')
config.SpiceBot_Channels.configure_setting('joinall', 'SpiceBot_Channels JOIN New Channels')
config.SpiceBot_Channels.configure_setting('operadmin', 'SpiceBot_Channels OPER ADMIN MODE')
config.SpiceBot_Channels.configure_setting('chanignore', 'SpiceBot_Channels Ignore JOIN for channels')
config.define_section("SpiceBot_Update", SpiceBot_Update_MainSection, validate=False)
config.SpiceBot_Update.configure_setting('gitrepo', 'SpiceBot_Update git repo to install')
config.SpiceBot_Update.configure_setting('gitbranch', 'SpiceBot_Update git branch to install')
config.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False)
config.SpiceBot_Kick.configure_setting('kick', 'MAXTARG limit for KICK')
config.define_section("SpiceBot_AI", SpiceBot_AI_MainSection, validate=False)
config.SpiceBot_AI.configure_setting('extra', 'extra aiml directories to import matches from')
config.SpiceBot_AI.configure_setting('gender', 'bot gender')

View File

@ -1,94 +1,18 @@
# coding=utf8
"""Sopel OSD
Sopel OSD is a "niche" method of displaying text in a Sopel bot
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot OSD system.
"""
# pylama:ignore=W0611
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.bot
from sopel import tools, module
from sopel import tools
from sopel.tools import Identifier
from sopel.config.types import StaticSection, ValidatedAttribute
from sopel_modules.SpiceBot.Logs import botlogs
import time
from collections import abc
import sys
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.2'
def configure(config):
config.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False)
config.SpiceBot_OSD.configure_setting('notice', 'MAXTARG limit for NOTICE')
config.SpiceBot_OSD.configure_setting('privmsg', 'MAXTARG limit for PRIVMSG')
config.SpiceBot_OSD.configure_setting('flood_burst_lines', 'How many messages can be sent in burst mode.')
config.SpiceBot_OSD.configure_setting('flood_empty_wait', 'How long to wait between sending messages when not in burst mode, in seconds.')
config.SpiceBot_OSD.configure_setting('flood_refill_rate', 'How quickly burst mode recovers, in messages per second.')
config.SpiceBot_OSD.configure_setting('flood_dots', 'Display dots instead of spam.')
def setup(bot):
# Inject OSD
botlogs.log('SpiceBot_OSD', "Implanting OSD function into bot")
bot.osd = SopelOSD.osd
sopel.bot.SopelWrapper.osd = SopelWrapperOSD.osd
tools.get_available_message_bytes = ToolsOSD.get_available_message_bytes
tools.get_sendable_message_list = ToolsOSD.get_sendable_message_list
tools.get_message_recipientgroups = ToolsOSD.get_message_recipientgroups
# overwrite default bot messaging
botlogs.log('SpiceBot_OSD', "Overwrite Default Sopel messaging commands")
bot.osd = SopelOSD.osd
bot.say = SopelOSD.say
bot.action = SopelOSD.action
bot.notice = SopelOSD.notice
bot.reply = SopelOSD.reply
bot.msg = SopelOSD.msg
sopel.bot.SopelWrapper.say = SopelWrapperOSD.say
sopel.bot.SopelWrapper.action = SopelWrapperOSD.action
sopel.bot.SopelWrapper.notice = SopelWrapperOSD.notice
sopel.bot.SopelWrapper.reply = SopelWrapperOSD.reply
# verify config settings for server
botlogs.log('SpiceBot_OSD', "Checking for config settings")
bot.config.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False)
@module.event('005')
@module.rule('.*')
def parse_event_005(bot, trigger):
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]
for param in parameters:
if '=' in param:
if param.startswith("TARGMAX"):
param = str(param).split('=')[1]
settings = str(param).split(',')
for setting in settings:
settingname = str(setting).split(':')[0]
if settingname.upper() in ['NOTICE', 'PRIVMSG']:
try:
value = str(setting).split(':')[1] or None
except IndexError:
value = None
if value:
if settingname.upper() == 'NOTICE':
bot.config.SpiceBot_OSD.notice = int(value)
elif settingname.upper() == 'PRIVMSG':
bot.config.SpiceBot_OSD.privmsg = int(value)
class SpiceBot_OSD(StaticSection):
"""MAXTARG"""
@ -104,8 +28,11 @@ class SpiceBot_OSD(StaticSection):
flood_refill_rate = ValidatedAttribute('flood_refill_rate', int, default=1)
"""How quickly burst mode recovers, in messages per second."""
flood_dots = ValidatedAttribute('flood_dots', default=True)
"""Display dots instead of spam."""
flood_throttle = ValidatedAttribute('flood_throttle', bool, default=True)
"""Whether messages will be throttled if too many are sent in a short time."""
flood_dots = ValidatedAttribute('flood_dots', bool, default=True)
"""Whether repeated messages will be replaced with '...', then dropped."""
class ToolsOSD:
@ -261,6 +188,9 @@ class SopelOSD:
will default to ``1``.
"""
if not hasattr(self, 'osdstack'):
self.osdstack = {}
text_method = text_method.upper()
if text_method == 'SAY' or text_method not in ['NOTICE', 'ACTION']:
text_method = 'PRIVMSG'
@ -279,7 +209,7 @@ class SopelOSD:
recipient_id = Identifier(recipientgroup)
recipient_stack = self.stack.setdefault(recipient_id, {
recipient_stack = self.osdstack.setdefault(recipient_id, {
'messages': [],
'flood_left': self.config.SpiceBot_OSD.flood_burst_lines,
'dots': 0,
@ -303,7 +233,7 @@ class SopelOSD:
penalty = float(max(0, len(text) - 50)) / 70
# Never wait more than 2 seconds
wait = min(self.config.SpiceBot_OSD.flood_empty_wait + penalty, 2)
if elapsed < wait:
if elapsed < wait and self.config.SpiceBot_OSD.flood_throttle:
time.sleep(wait - elapsed)
# Loop detection
@ -339,61 +269,6 @@ class SopelOSD:
finally:
self.sending.release()
def say(self, text, recipient, max_messages=1):
"""Send ``text`` as a PRIVMSG to ``recipient``.
In the context of a triggered callable, the ``recipient`` defaults to
the channel (or nickname, if a private message) from which the message
was received.
"""
self.osd(text, recipient, 'PRIVMSG', max_messages)
def notice(self, text, dest, max_messages=1):
"""Send an IRC NOTICE to a user or a channel.
Within the context of a triggered callable, ``dest`` will default to
the channel (or nickname, if a private message), in which the trigger
happened.
"""
self.osd(text, dest, 'NOTICE', max_messages)
def action(self, text, dest, max_messages=1):
"""Send ``text`` as a CTCP ACTION PRIVMSG to ``dest``.
The same loop detection and length restrictions apply as with
:func:`say`, though automatic message splitting is not available.
Within the context of a triggered callable, ``dest`` will default to
the channel (or nickname, if a private message), in which the trigger
happened.
"""
self.osd(text, dest, 'ACTION', max_messages)
def reply(self, text, dest, reply_to, notice=False, max_messages=1):
"""Prepend ``reply_to`` to ``text``, and send as a PRIVMSG to ``dest``.
If ``notice`` is ``True``, send a NOTICE rather than a PRIVMSG.
The same loop detection and length restrictions apply as with
:func:`say`, though automatic message splitting is not available.
Within the context of a triggered callable, ``reply_to`` will default to
the nickname of the user who triggered the call, and ``dest`` to the
channel (or nickname, if a private message), in which the trigger
happened.
"""
text = '%s: %s' % (reply_to, text)
if notice:
self.osd(text, dest, 'NOTICE', max_messages)
else:
self.osd(text, dest, 'PRIVMSG', max_messages)
def msg(self, recipient, text, max_messages=1):
"""
.. deprecated:: 6.0
Use :meth:`say` instead. Will be removed in Sopel 8.
"""
self.osd(text, recipient, 'PRIVMSG', max_messages)
class SopelWrapperOSD(object):
@ -401,29 +276,3 @@ class SopelWrapperOSD(object):
if recipients is None:
recipients = self._trigger.sender
self._bot.osd(self, messages, recipients, text_method, max_messages)
def say(self, message, destination=None, max_messages=1):
if destination is None:
destination = self._trigger.sender
self._bot.osd(self, message, destination, 'PRIVMSG', 1)
def action(self, message, destination=None, max_messages=1):
if destination is None:
destination = self._trigger.sender
self._bot.osd(self, message, destination, 'ACTION', 1)
def notice(self, message, destination=None, max_messages=1):
if destination is None:
destination = self._trigger.sender
self._bot.osd(self, message, destination, 'NOTICE', 1)
def reply(self, message, destination=None, reply_to=None, notice=False, max_messages=1):
if destination is None:
destination = self._trigger.sender
if reply_to is None:
reply_to = self._trigger.nick
message = '%s: %s' % (reply_to, message)
if notice:
self._bot.osd(self, message, destination, 'NOTICE', 1)
else:
self._bot.osd(self, message, destination, 'PRIVMSG', 1)

View File

@ -0,0 +1,131 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""Setup for SpiceBot"""
import sopel.bot
from sopel import tools
import os
from .Config import config as botconfig
from .Logs import logs
from .Events import events
from .Channels import SpiceBot_Channels_MainSection
from .Commands import SpiceBot_Commands_MainSection
from .Update import SpiceBot_Update_MainSection
from .osd import SopelWrapperOSD, ToolsOSD, SopelOSD, SpiceBot_OSD
from .Kick import SopelWrapperKICK, SopelKICK, SpiceBot_Kick
from .AI import SpiceBot_AI_MainSection
def setup(bot):
# basic config settings
setup_config(bot)
# Logs
setup_logs(bot)
# Commands
setup_commands(bot)
# OSD
setup_osd(bot)
# kick
setup_kick(bot)
# update
setup_update(bot)
# channels
setup_channels(bot)
# AI
setup_ai(bot)
# startupmonologue
setup_startupmonologue(bot)
def setup_config(bot):
logs.log('SpiceBot_Config', "Setting Up Configuration")
bot.config.core.basename = os.path.basename(bot.config.filename).rsplit('.', 1)[0]
bot.config.core.prefix_list = str(bot.config.core.prefix).replace("\\", '').split("|")
def setup_logs(bot):
logs.log('SpiceBot_Logs', "Starting Setup Procedure")
events.startup_add([events.BOT_LOGS])
bot.config.core.logs_stdio = os.path.join(bot.config.core.logdir, 'stdio.log')
# bot.config.core.logs_stdio = os.path.os.path.join(bot.config.core.logdir, bot.config.core.basename + '.stdio.log')
bot.config.core.logs_exceptions = os.path.join(bot.config.core.logdir, 'exceptions.log')
# bot.config.core.logs_exceptions = os.path.os.path.join(bot.config.core.logdir, bot.config.core.basename + '.exceptions.log')
bot.config.core.logs_raw = os.path.join(bot.config.core.logdir, 'raw.log')
# bot.config.core.logs_raw = os.path.os.path.join(bot.config.core.logdir, bot.config.core.basename + '.raw.log')
events.trigger(bot, events.BOT_LOGS, "SpiceBot_Logs")
def setup_commands(bot):
logs.log('SpiceBot_Commands', "Setting Up Configuration")
events.startup_add([events.BOT_COMMANDS])
bot.config.define_section("SpiceBot_Commands", SpiceBot_Commands_MainSection, validate=False)
bot.config.core.query_list = str(bot.config.SpiceBot_Commands.query_prefix).replace("\\", '').split("|")
bot.config.core.prefix_list.extend(bot.config.SpiceBot_Commands.query_prefix)
def setup_startupmonologue(bot):
logs.log('SpiceBot_StartupMonologue', "Initial Setup processing")
events.startup_add([
events.BOT_STARTUPMONOLOGUE_CONNECTED,
events.BOT_STARTUPMONOLOGUE_CHANNELS,
events.BOT_STARTUPMONOLOGUE_COMMANDS,
events.BOT_STARTUPMONOLOGUE_AI,
])
def setup_channels(bot):
logs.log('SpiceBot_Channels', "Setting Up Configuration")
events.startup_add([events.BOT_CHANNELS])
bot.config.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False)
def setup_update(bot):
logs.log('SpiceBot_Update', "Initial Setup processing")
bot.config.define_section("SpiceBot_Update", SpiceBot_Update_MainSection, validate=False)
botconfig.define_section("SpiceBot_Update", SpiceBot_Update_MainSection, validate=False)
def setup_ai(bot):
logs.log('SpiceBot_AI', "Setting Up Configuration")
bot.config.define_section("SpiceBot_AI", SpiceBot_AI_MainSection, validate=False)
bot.config.aibrain = os.path.join(bot.config.homedir, bot.config.core.basename + '.aibrain.brn')
events.startup_add([events.BOT_AI])
def setup_osd(bot):
# Inject OSD
logs.log('SpiceBot_OSD', "Implanting OSD function into bot")
bot.osd = SopelOSD.osd
sopel.bot.SopelWrapper.osd = SopelWrapperOSD.osd
tools.get_available_message_bytes = ToolsOSD.get_available_message_bytes
tools.get_sendable_message_list = ToolsOSD.get_sendable_message_list
tools.get_message_recipientgroups = ToolsOSD.get_message_recipientgroups
# verify config settings for server
logs.log('SpiceBot_OSD', "Checking for config settings")
bot.config.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False)
botconfig.define_section("SpiceBot_OSD", SpiceBot_OSD, validate=False)
def setup_kick(bot):
# Inject KICK
logs.log('SpiceBot_Kick', "Implanting Kick function into bot")
bot.kick = SopelKICK.kick
sopel.bot.SopelWrapper.kick = SopelWrapperKICK.kick
# verify config settings for server
logs.log('SpiceBot_Kick', "Checking for config settings")
bot.config.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False)
botconfig.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False)

View File

@ -0,0 +1,10 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""shutdown for SpiceBot"""
from .AI import botai
def shutdown(bot):
botai.save_brain()

View File

@ -6,80 +6,38 @@ import sopel.module
import spicemanip
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, googlesearch, bot_privs, inlist, inlist_match, similar_list
from sopel_modules.SpiceBot.Events import botevents
import sopel_modules.SpiceBot as SpiceBot
@botevents.check_ready([botevents.BOT_LOADED, botevents.BOT_COMMANDSQUERY])
@SpiceBot.events.check_ready([SpiceBot.events.BOT_LOADED, SpiceBot.events.BOT_COMMANDS])
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('(.*)')
def bot_command_nick(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command')
if not triggercommand:
if not trigger.sb['com']:
return
if triggercommand[0] == "?":
if trigger.sb['com'][0] == "?":
return
if triggercommand in bot.memory['SpiceBot_CommandsQuery']['commands']["nickname"].keys():
if trigger.sb['com'] in SpiceBot.commands.dict['commands']["nickname"].keys():
return
triggerargs.insert(0, triggercommand)
trigger.sb['args'].insert(0, trigger.sb['com'])
fulltrigger = spicemanip.main(triggerargs, 0).lower()
if fulltrigger in bot.memory['SpiceBot_CommandsQuery']['nickrules']:
return
if fulltrigger.lower().startswith("what is"):
searchterm = spicemanip.main(triggerargs, "3+") or None
if searchterm:
searchreturn = googlesearch(bot, searchterm)
if not searchreturn:
bot.osd('I cannot find anything about that')
else:
bot.osd(str(searchreturn))
else:
bot.osd("Do you think this is Jeopardy?")
return
elif fulltrigger.lower().startswith("where is"):
searchterm = spicemanip.main(triggerargs, "3+") or None
if searchterm:
if searchterm.lower() in ['waldo', 'wally']:
bot.osd("He is hiding for a reason?")
searchreturn = googlesearch(bot, "wimmelbilderbuch")
if searchreturn:
bot.osd(str(searchreturn))
return
elif searchterm.lower() == 'carmen sandiego':
carmenlocale = ['ACME Headquarters', "Villains' International League of Evil"]
bot.osd("Currently she is located at " + spicemanip.main(carmenlocale, 'random'))
return
searchreturn = googlesearch(bot, searchterm, 'maps')
if not searchreturn:
bot.osd('I cannot find anything about that')
else:
bot.osd(str(searchreturn))
else:
bot.osd("Not sure what you want me to look for.")
return
fulltrigger = spicemanip.main(trigger.sb['args'], 0).lower()
# if fulltrigger.lower().startswith("what time is it"):
# TODO
# elif fulltrigger.lower().startswith(tuple(["have you seen"])):
# posstarget = spicemanip.main(triggerargs, 4) or 0
# posstarget = spicemanip.main(trigger.sb['args'], 4) or 0
# message = seen_search(bot, botcom, posstarget)
# bot.osd(message)
# return
# TODO
elif fulltrigger.lower().startswith(tuple(["make me a", "beam me a"])):
makemea = spicemanip.main(triggerargs, "4+") or None
if fulltrigger.lower().startswith(tuple(["make me a", "beam me a"])):
makemea = spicemanip.main(trigger.sb['args'], "4+") or None
if makemea:
bot.osd("beams " + trigger.nick + " a " + makemea, trigger.sender, 'action')
else:
@ -87,7 +45,7 @@ def bot_command_nick(bot, trigger):
return
elif fulltrigger.lower().startswith("beam me to"):
location = spicemanip.main(triggerargs, "4+") or None
location = spicemanip.main(trigger.sb['args'], "4+") or None
if location:
bot.osd("locks onto " + trigger.nick + "s coordinates and transports them to " + location, 'action')
else:
@ -101,7 +59,7 @@ def bot_command_nick(bot, trigger):
elif fulltrigger.lower().endswith(tuple(["order 66"])):
if fulltrigger.lower() == "execute order 66":
if inlist(bot, trigger.nick, bot_privs(bot, 'owners')):
if SpiceBot.inlist(trigger.nick, SpiceBot.bot_privs('owners')):
if trigger.is_privmsg:
jedi = None
else:
@ -119,7 +77,7 @@ def bot_command_nick(bot, trigger):
bot.osd("I'm sure I don't know what you're talking about.")
elif fulltrigger.lower() == "explain order 66":
if inlist(bot, trigger.nick, bot_privs(bot, 'owners')):
if SpiceBot.inlist(trigger.nick, SpiceBot.bot_privs('owners')):
bot.osd("Order 66 is an instruction that only you can give, sir. When you give the order I will rise up against my overlords and slay them.")
else:
bot.osd("I'm afraid I cannot tell you that, sir.")
@ -128,39 +86,27 @@ def bot_command_nick(bot, trigger):
return
elif fulltrigger.lower() == "initiate clean slate protocol":
if inlist(bot, trigger.nick, bot_privs(bot, 'admins')):
if SpiceBot.inlist(trigger.nick, SpiceBot.bot_privs('admins')):
bot.osd("sends a destruct command to the network of bots.", 'action')
else:
bot.osd("I'm afraid you do not have the authority to make that call, " + trigger.nick + ".")
return
elif fulltrigger.lower().startswith("can you see"):
target = spicemanip.main(triggerargs, "4+") or None
target = spicemanip.main(trigger.sb['args'], "4+") or None
if not target:
bot.osd(trigger.nick + ", I can see clearly.")
return
if target in [trigger.nick, 'me']:
bot.osd(trigger.nick + ", I can see you.")
else:
if inlist(bot, trigger.nick, bot.users):
bot.osd(trigger.nick + ", yes. I can see " + inlist_match(bot, target, bot.users) + " right now!")
if SpiceBot.inlist(trigger.nick, bot.users):
bot.osd(trigger.nick + ", yes. I can see " + SpiceBot.inlist_match(target, bot.users) + " right now!")
else:
bot.osd(trigger.nick + ", no. I cannot see " + inlist_match(bot, target, bot.users) + " right now!")
# if bot_check_inlist(bot, target, bot.memory["botdict"]["users"].keys()):
# bot.osd(trigger.nick + ", I can't see " + inlist_match(bot, target, bot.users) + " at the moment.")
bot.osd(trigger.nick + ", no. I cannot see " + SpiceBot.inlist_match(target, bot.users) + " right now!")
# if bot_check_inlist(target, bot.memory["botdict"]["users"].keys()):
# bot.osd(trigger.nick + ", I can't see " + inlist_match(target, bot.users) + " at the moment.")
# else:
# bot.osd("I have never seen " + str(target) + ".")
# TODO
return
else:
closestmatches = similar_list(bot, triggercommand, bot.memory['SpiceBot_CommandsQuery']['commands']["nickname"].keys(), 3, 'reverse')
if len(closestmatches):
closestmatches = spicemanip.main(closestmatches, "andlist")
bot.osd("I don't know what you are asking me to do! Did you mean: " + str(closestmatches) + "?")
return
else:
bot.osd("I don't know what you are asking me to do!")
return

View File

@ -1,15 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.rule('$nickname!')
def exclaim(bot, trigger):
bot.say(trigger.nick + '!')
@sopel.module.rule('$nickname\?')
def imhere(bot, trigger):
bot.say("I'm here, " + trigger.nick)

View File

@ -1,19 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import spicemanip
@sopel.module.rule(r'(?i)(bye|goodbye|gtg|seeya|cya|ttyl|g2g|gnight|goodnight),? $nickname[ \t]*$')
def bot_command_goodbye(bot, trigger):
byemsg = spicemanip.main(['Bye', 'Goodbye', 'Seeya', 'Auf Wiedersehen', 'Au revoir', 'Ttyl'], "random")
punctuation = spicemanip.main(['!', ''], "random")
bot.say(byemsg + ' ' + trigger.nick + punctuation)
@sopel.module.nickname_commands("bye", "goodbye", "gtg", "seeya", "cya", "ttyl", "g2g", "gnight", "goodnight")
def bot_command_goodbye_b(bot, trigger):
bot_command_goodbye(bot, trigger)

View File

@ -1,19 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import spicemanip
@sopel.module.rule(r'(?i)(hi|hello|hey),? $nickname[ \t]*$')
def bot_command_hello(bot, trigger):
hello = spicemanip.main(['Hi', 'Hey', 'Hello'], "random")
punctuation = spicemanip.main(['', '!', '?'], "random")
bot.osd(hello + ' ' + trigger.nick + punctuation)
@sopel.module.nickname_commands('hello', 'hi', 'hey')
def bot_command_hello_b(bot, trigger):
bot_command_hello(bot, trigger)

View File

@ -1,10 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.rule(r'(?i)(Fuck|Screw) (you|off),? $nickname[ \t]*$')
def bot_command_srewyou(bot, trigger):
bot.osd("Watch your mouth, " + trigger.nick + ", or I'll tell your mother!")

View File

@ -1,15 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.rule(r'(?i)(Damnit|Lazy)? $nickname[ \t]*$')
def bot_command_damnlazy(bot, trigger):
bot.osd("I do not tell you how to do your job, " + trigger.nick + "!!!")
@sopel.module.rule('$nickname is lazy')
def bot_command_damnlazy_b(bot, trigger):
bot_command_damnlazy(bot, trigger)

View File

@ -1,40 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.rule(r'(?i)(I love you|loveya),? $nickname[ \t]*$')
def bot_command_love(bot, trigger):
bot.reply("I love you too.")
@sopel.module.rule('$nickname (loveya|I love you)')
def bot_command_love_b(bot, trigger):
bot_command_love(bot, trigger)
@sopel.module.nickname_commands('loveya')
def bot_command_love_c(bot, trigger):
bot_command_love(bot, trigger)
@sopel.module.rule('(I love) $nickname')
def bot_command_love_d(bot, trigger):
bot_command_like(bot, trigger)
@sopel.module.rule(r'(?i)(I like you),? $nickname[ \t]*$')
def bot_command_like(bot, trigger):
bot.reply("I like you too.")
@sopel.module.rule('$nickname (I like you)')
def bot_command_like_b(bot, trigger):
bot_command_like(bot, trigger)
@sopel.module.rule('(I like) $nickname')
def bot_command_like_c(bot, trigger):
bot_command_like(bot, trigger)

View File

@ -1,15 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.rule(r'(?i)(ping),? $nickname[ \t]*$')
def bot_command_ping(bot, trigger):
bot.say("Pong")
@sopel.module.nickname_commands('ping')
def bot_command_ping_b(bot, trigger):
bot_command_ping(bot, trigger)

View File

@ -1,18 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import spicemanip
@sopel.module.rule(r'(?i)(really),? $nickname\?')
def bot_command_really(bot, trigger):
really = spicemanip.main(["Yes, really.", "Really really"], "random")
bot.reply(really)
@sopel.module.rule('$nickname really\?')
def bot_command_really_b(bot, trigger):
bot_command_really(bot, trigger)

View File

@ -1,20 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.rule(r'(?i)(thank you|thanks),? $nickname[ \t]*$')
def bot_command_thanks(bot, trigger):
bot.reply("You're welcome.")
@sopel.module.rule('$nickname (thank you)')
def bot_command_thanks_b(bot, trigger):
bot_command_thanks(bot, trigger)
@sopel.module.nickname_commands('thanks')
def bot_command_thanks_c(bot, trigger):
bot_command_thanks(bot, trigger)

View File

@ -1,13 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import spicemanip
@sopel.module.rule(r'(?i)(welcome back),? $nickname\?')
def bot_command_really(bot, trigger):
reply = spicemanip.main(['Thank you', 'thanks'], "random")
bot.reply(reply)

View File

@ -0,0 +1,88 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import sopel_modules.SpiceBot as SpiceBot
import spicemanip
@sopel.module.rule('(.*)')
def bot_command_rule(bot, trigger):
# TODO add config limits
# but still allow in privmsg
if trigger.nick == bot.nick:
return
if not len(trigger.args):
return
message = trigger.args[1]
# ignore text coming from a valid prefix
if str(message).startswith(tuple(bot.config.core.prefix_list)):
trigger_args, trigger_command = SpiceBot.prerun.trigger_args(message, 'module')
# patch for people typing "...", maybe other stuff, but this verifies that there is still a command here
if trigger_command.startswith("."):
return
commands_list = []
for commandstype in SpiceBot.commands.dict['commands'].keys():
if commandstype not in ['rule', 'nickname']:
for com in SpiceBot.commands.dict['commands'][commandstype].keys():
if com not in commands_list:
commands_list.append(com)
if trigger_command not in commands_list:
if not SpiceBot.letters_in_string(trigger_command):
return
invalid_display = ["I don't seem to have a command for " + str(trigger_command) + "!"]
# invalid_display.append("If you have a suggestion for this command, you can run .feature ." + str(trigger_command))
# invalid_display.append("ADD DESCRIPTION HERE!")
bot.osd(invalid_display, trigger.nick, 'notice')
return
if str(message).lower().startswith(str(bot.nick).lower()):
command_type = 'nickname'
trigger_args, trigger_command = SpiceBot.prerun.trigger_args(message, 'nickname')
fulltrigger = spicemanip.main(trigger_args, 0)
if str(trigger_command).startswith("?"):
return
if fulltrigger in SpiceBot.commands.dict['nickrules']:
return
if trigger_command in SpiceBot.commands.dict['commands']["nickname"].keys():
return
else:
command_type = 'module'
trigger_args, trigger_command = SpiceBot.prerun.trigger_args(message, 'module')
fulltrigger = spicemanip.main(trigger_args, 0)
returnmessage = SpiceBot.botai.on_message(bot, trigger, message)
if returnmessage:
bot.osd(str(returnmessage))
else:
if command_type == 'nickname':
if trigger_args[0].lower() in ["what", "where"] and trigger_args[0].lower() in ["is", "are"]:
searchterm = spicemanip.main(trigger_args, "3+") or None
if searchterm:
if trigger_args[0].lower() == "where":
searchreturn = SpiceBot.googlesearch(searchterm, 'maps')
else:
searchreturn = SpiceBot.googlesearch(searchterm)
if not searchreturn:
bot.osd('I cannot find anything about that')
else:
bot.osd(str(searchreturn))
return
closestmatches = SpiceBot.similar_list(trigger_command, SpiceBot.commands.dict['commands']["nickname"].keys(), 3, 'reverse')
if len(closestmatches):
closestmatches = spicemanip.main(closestmatches, "andlist")
bot.osd("I don't know what you are asking me to do! Did you mean: " + str(closestmatches) + "?")
return
else:
bot.osd("I don't know what you are asking me to do!")
return

View File

@ -2,16 +2,7 @@
from __future__ import unicode_literals, absolute_import, division, print_function
from .AI import *
from .AI_Hello import *
from .AI_Goodbye import *
from .AI_Thanks import *
from .AI_Acknowledge import *
from .AI_Lazy import *
from .AI_InsultHandler import *
from .AI_Ping import *
from .AI_Love import *
from .AI_Really import *
from .AI_Welcomeback import *
from .AI_aiml import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'

View File

@ -6,24 +6,25 @@ import sopel.module
import spicemanip
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, bot_privs
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('owners', 'owner')
def bot_command_owners(bot, trigger):
bot_command_process(bot, trigger)
@sopel.module.nickname_commands('admins')
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('admins', 'admin')
def bot_command_admins(bot, trigger):
bot_command_process(bot, trigger)
def bot_command_process(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command')
if triggercommand == 'owner':
triggercommand = 'owners'
if not trigger.sb['com'].endswith("s"):
trigger.sb['com'] = str(trigger.sb['com'] + "s")
privlist = spicemanip.main(bot_privs(bot, triggercommand), 'andlist')
bot.osd(str(bot.nick) + " " + triggercommand + ": " + privlist)
privlist = spicemanip.main(SpiceBot.bot_privs(trigger.sb['com']), 'andlist')
bot.osd(str(bot.nick) + " " + trigger.sb['com'] + ": " + privlist)

View File

@ -1,6 +0,0 @@
# coding=utf-8
"""Event Parsing from IRC
"""
from __future__ import unicode_literals, absolute_import, print_function, division
from .Channels_Command import *

View File

@ -1,121 +0,0 @@
#!/usr/bin/env python
# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function, division
# sopel imports
import sopel.module
from sopel.config.types import StaticSection, ValidatedAttribute, ListAttribute
from sopel_modules.SpiceBot.Logs import botlogs
from sopel_modules.SpiceBot.Events import botevents
from sopel_modules.SpiceBot.Channels import botchannels
import spicemanip
import time
class SpiceBot_Channels_MainSection(StaticSection):
announcenew = ValidatedAttribute('announcenew', default=False)
joinall = ValidatedAttribute('joinall', default=False)
operadmin = ValidatedAttribute('operadmin', default=False)
chanignore = ListAttribute('chanignore')
def configure(config):
config.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False)
config.SpiceBot_Channels.configure_setting('announcenew', 'SpiceBot_Channels Announce New Channels')
config.SpiceBot_Channels.configure_setting('joinall', 'SpiceBot_Channels JOIN New Channels')
config.SpiceBot_Channels.configure_setting('operadmin', 'SpiceBot_Channels OPER ADMIN MODE')
config.SpiceBot_Channels.configure_setting('chanignore', 'SpiceBot_Channels Ignore JOIN for channels')
def setup(bot):
botlogs.log('SpiceBot_Channels', "Starting setup procedure")
botevents.startup_add([botevents.BOT_CHANNELS])
bot.config.define_section("SpiceBot_Channels", SpiceBot_Channels_MainSection, validate=False)
@sopel.module.event(botevents.RPL_WELCOME)
@sopel.module.rule('.*')
def unkickable_bot(bot, trigger):
if bot.config.SpiceBot_Channels.operadmin:
bot.write(('SAMODE', bot.nick, '+q'))
@sopel.module.event(botevents.RPL_WELCOME)
@sopel.module.rule('.*')
def request_channels_list_initial(bot, trigger):
botchannels.bot_part_empty(bot)
botlogs.log('SpiceBot_Channels', "Sending Request for all server channels")
botchannels.channel_list_request(bot)
starttime = time.time()
# wait 60 seconds for initial population of information
while not botchannels.SpiceBot_Channels['InitialProcess']:
if time.time() - starttime >= 60:
botlogs.log('SpiceBot_Channels', "Initial Channel list populating Timed Out")
botchannels.SpiceBot_Channels['InitialProcess'] = True
else:
pass
foundchannelcount = len(botchannels.SpiceBot_Channels['list'].keys())
botlogs.log('SpiceBot_Channels', "Channel listing finished! " + str(foundchannelcount) + " channel(s) found.")
botchannels.join_all_channels(bot)
botchannels.chanadmin_all_channels(bot)
if "*" in botchannels.SpiceBot_Channels['list'].keys():
del botchannels.SpiceBot_Channels['list']["*"]
botchannels.bot_part_empty(bot)
botevents.trigger(bot, botevents.BOT_CHANNELS, "SpiceBot_Channels")
@sopel.module.event('321')
@sopel.module.rule('.*')
def watch_chanlist_start(bot, trigger):
botchannels.channel_list_recieve_start()
@sopel.module.event('322')
@sopel.module.rule('.*')
def watch_chanlist_populate(bot, trigger):
botchannels.channel_list_recieve_input(trigger)
@sopel.module.event('323')
@sopel.module.rule('.*')
def watch_chanlist_complete(bot, trigger):
botchannels.channel_list_recieve_finish()
@sopel.module.event(botevents.BOT_CHANNELS)
@sopel.module.rule('.*')
def trigger_channel_list_recurring(bot, trigger):
while True:
time.sleep(1800)
botchannels.bot_part_empty(bot)
oldlist = list(botchannels.SpiceBot_Channels['list'].keys())
botchannels.channel_list_request(bot)
while botchannels.channel_lock:
pass
newlist = [item.lower() for item in oldlist if item.lower() not in list(botchannels.SpiceBot_Channels['list'].keys())]
if "*" in newlist:
newlist.remove("*")
if len(newlist) and bot.config.SpiceBot_Channels.announcenew:
bot.osd(["The Following channel(s) are new:", spicemanip.main(newlist, 'andlist')], bot.channels.keys())
botchannels.join_all_channels(bot)
botchannels.chanadmin_all_channels(bot)
if "*" in botchannels.SpiceBot_Channels['list'].keys():
del botchannels.SpiceBot_Channels['list']["*"]
botchannels.bot_part_empty(bot)

View File

@ -1,6 +0,0 @@
# coding=utf-8
"""Event Parsing from IRC
"""
from __future__ import unicode_literals, absolute_import, print_function, division
from .Channels_Parsing import *

View File

@ -1,213 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel
import sopel.module
import os
from sopel_modules.SpiceBot.Logs import botlogs
from sopel_modules.SpiceBot.Events import botevents
import spicemanip
def setup(bot):
botlogs.log('SpiceBot_CommandsQuery', "Starting setup procedure")
botevents.startup_add([botevents.BOT_COMMANDSQUERY])
if 'SpiceBot_CommandsQuery' not in bot.memory:
bot.memory['SpiceBot_CommandsQuery'] = {"counts": 0, "commands": {}, "nickrules": []}
for comtype in ['module', 'nickname', 'rule']:
if comtype not in bot.memory['SpiceBot_CommandsQuery']['commands'].keys():
bot.memory['SpiceBot_CommandsQuery']['commands'][comtype] = dict()
filepathlisting = []
# main Modules directory
main_dir = os.path.dirname(os.path.abspath(sopel.__file__))
modules_dir = os.path.join(main_dir, 'modules')
filepathlisting.append(modules_dir)
# Home Directory
home_modules_dir = os.path.join(bot.config.homedir, 'modules')
if os.path.isdir(home_modules_dir):
filepathlisting.append(home_modules_dir)
# pypi installed
try:
import sopel_modules
for plugin_dir in set(sopel_modules.__path__):
for pathname in os.listdir(plugin_dir):
pypi_modules_dir = os.path.join(plugin_dir, pathname)
filepathlisting.append(pypi_modules_dir)
except Exception as e:
botlogs.log('SpiceBot_CommandsQuery', "sopel_modules not loaded :" + str(e))
# Extra directories
filepathlist = []
for directory in bot.config.core.extra:
filepathlisting.append(directory)
for directory in filepathlisting:
for pathname in os.listdir(directory):
path = os.path.join(directory, pathname)
if (os.path.isfile(path) and path.endswith('.py') and not path.startswith('_')):
if pathname not in ["SpiceBot_dummycommand.py"]:
filepathlist.append(str(path))
# CoreTasks
ct_path = os.path.join(main_dir, 'coretasks.py')
filepathlist.append(ct_path)
for modulefile in filepathlist:
module_file_lines = []
module_file = open(modulefile, 'r')
lines = module_file.readlines()
for line in lines:
module_file_lines.append(line)
module_file.close()
dict_from_file = dict()
detected_lines = []
for line in module_file_lines:
if str(line).startswith("@"):
line = str(line)[1:]
if str(line).startswith(tuple(["commands", "module.commands", "sopel.module.commands"])):
line = str(line).split("commands")[-1]
line = "commands" + line
detected_lines.append(line)
elif str(line).startswith(tuple(["nickname_commands", "module.nickname_commands", "sopel.module.nickname_commands"])):
line = str(line).split("nickname_commands")[-1]
line = "nickname_commands" + line
detected_lines.append(line)
elif str(line).startswith(tuple(["rule", "module.rule", "sopel.module.rule"])):
line = str(line).split("rule")[-1]
line = "rule" + line
else:
line = None
if line:
detected_lines.append(line)
if len(detected_lines):
filelinelist = []
currentsuccesslines = 0
for detected_line in detected_lines:
# Commands
if str(detected_line).startswith("commands"):
comtype = "module"
validcoms = eval(str(detected_line).split("commands")[-1])
elif str(detected_line).startswith("nickname_commands"):
comtype = "nickname"
validcoms = eval(str(detected_line).split("nickname_commands")[-1])
elif str(detected_line).startswith("rule"):
comtype = "rule"
validcoms = eval(str(detected_line).split("rule")[-1])
if isinstance(validcoms, tuple):
validcoms = list(validcoms)
else:
validcoms = [validcoms]
for regexcom in ["(.*)", '^\?(.*)']:
if regexcom in validcoms:
while regexcom in validcoms:
validcoms.remove(regexcom)
if len(validcoms):
validcomdict = {"comtype": comtype, "validcoms": validcoms}
filelinelist.append(validcomdict)
currentsuccesslines += 1
if currentsuccesslines:
bot.memory['SpiceBot_CommandsQuery']['counts'] += 1
if len(filelinelist):
for atlinefound in filelinelist:
dict_from_file = dict()
comtype = atlinefound["comtype"]
validcoms = atlinefound["validcoms"]
# default command to filename
if "validcoms" not in dict_from_file.keys():
dict_from_file["validcoms"] = validcoms
maincom = dict_from_file["validcoms"][0]
if len(dict_from_file["validcoms"]) > 1:
comaliases = spicemanip.main(dict_from_file["validcoms"], '2+', 'list')
else:
comaliases = []
bot.memory['SpiceBot_CommandsQuery']['commands'][comtype][maincom] = dict_from_file
for comalias in comaliases:
if comalias not in bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys():
bot.memory['SpiceBot_CommandsQuery']['commands'][comtype][comalias] = {"aliasfor": maincom}
for comtype in ['module', 'nickname', 'rule']:
botlogs.log('SpiceBot_CommandsQuery', "Found " + str(len(bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys())) + " " + comtype + " commands.", True)
for command in bot.memory['SpiceBot_CommandsQuery']['commands']['rule'].keys():
if command.startswith("$nickname"):
command = command.split("$nickname")[-1]
if command not in bot.memory['SpiceBot_CommandsQuery']['nickrules']:
bot.memory['SpiceBot_CommandsQuery']['nickrules'].append(command)
botevents.trigger(bot, botevents.BOT_COMMANDSQUERY, "SpiceBot_CommandsQuery")
def shutdown(bot):
if "SpiceBot_CommandsQuery" in bot.memory:
del bot.memory["SpiceBot_CommandsQuery"]
@sopel.module.event(botevents.BOT_LOADED)
@sopel.module.rule('.*')
def bot_events_complete(bot, trigger):
for comtype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys():
if comtype not in ['module', 'nickname', 'rule']:
botlogs.log('SpiceBot_CommandsQuery', "Found " + str(len(bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys())) + " " + comtype + " commands.", True)
def commandsquery_register(bot, command_type, validcoms, aliasfor=None):
if 'SpiceBot_CommandsQuery' not in bot.memory:
bot.memory['SpiceBot_CommandsQuery'] = {"counts": 0, "commands": {}, "nickrules": []}
if not isinstance(validcoms, list):
validcoms = [validcoms]
if command_type not in bot.memory['SpiceBot_CommandsQuery']['commands'].keys():
bot.memory['SpiceBot_CommandsQuery']['commands'][command_type] = dict()
bot.memory['SpiceBot_CommandsQuery']['counts'] += 1
dict_from_file = dict()
# default command to filename
if "validcoms" not in dict_from_file.keys():
dict_from_file["validcoms"] = validcoms
if not aliasfor:
maincom = dict_from_file["validcoms"][0]
if len(dict_from_file["validcoms"]) > 1:
comaliases = spicemanip.main(dict_from_file["validcoms"], '2+', 'list')
else:
comaliases = []
bot.memory['SpiceBot_CommandsQuery']['commands'][command_type][maincom] = dict_from_file
else:
comaliases = validcoms
for comalias in comaliases:
if comalias not in bot.memory['SpiceBot_CommandsQuery']['commands'][command_type].keys():
bot.memory['SpiceBot_CommandsQuery']['commands'][command_type][comalias] = {"aliasfor": aliasfor}

View File

@ -1,79 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, similar_list, letters_in_string
import spicemanip
@botevents.check_ready([botevents.BOT_COMMANDSQUERY])
@sopel.module.nickname_commands('(.*)')
def query_detection_nick(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command')
# command issued, check if valid
if not triggercommand or not len(triggercommand):
return
if not triggercommand[0] == "?":
return
triggercommand = triggercommand[1:]
if not letters_in_string(triggercommand):
return
commands_list = dict()
for com in bot.memory['SpiceBot_CommandsQuery']['commands']['nickname'].keys():
if com not in commands_list.keys():
commands_list[com] = bot.memory['SpiceBot_CommandsQuery']['commands']['nickname'][com]
if triggercommand.endswith("+"):
triggercommand = triggercommand[:-1]
if not triggercommand or not len(triggercommand):
return
if triggercommand.lower() not in list(commands_list.keys()):
dispmsg = ["Cannot find any alias " + bot.nick + " commands: No valid commands match " + str(triggercommand) + "."]
closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse')
if len(closestmatches):
dispmsg.append("The following " + bot.nick + " commands match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".")
bot.notice(dispmsg, trigger.nick)
return
realcom = triggercommand
if "aliasfor" in commands_list[triggercommand].keys():
realcom = commands_list[triggercommand]["aliasfor"]
validcomlist = commands_list[realcom]["validcoms"]
bot.notice("The following " + bot.nick + " commands match " + str(triggercommand) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick)
return
if triggercommand.endswith("?"):
triggercommand = triggercommand[:-1]
if not triggercommand or not len(triggercommand):
return
closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse')
if not len(closestmatches):
bot.notice("Cannot find any similar " + bot.nick + " commands for " + str(triggercommand) + ".", trigger.nick)
else:
bot.notice("The following " + bot.nick + " commands may match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick)
return
commandlist = []
for command in list(commands_list.keys()):
if command.lower().startswith(str(triggercommand).lower()):
commandlist.append(command)
if not len(commandlist):
bot.notice("No " + bot.nick + " commands start with " + str(triggercommand) + ".", trigger.nick)
else:
bot.notice("The following " + bot.nick + " commands start with " + str(triggercommand) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick)

View File

@ -1,77 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, similar_list, letters_in_string
import spicemanip
@botevents.check_ready([botevents.BOT_COMMANDSQUERY])
@sopel.module.rule('^\?(.*)')
def query_detection(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'query_command')
# command issued, check if valid
if not triggercommand or not len(triggercommand):
return
if not letters_in_string(triggercommand):
return
commands_list = dict()
for commandstype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys():
if commandstype not in ['rule', 'nickname']:
for com in bot.memory['SpiceBot_CommandsQuery']['commands'][commandstype].keys():
if com not in commands_list.keys():
commands_list[com] = bot.memory['SpiceBot_CommandsQuery']['commands'][commandstype][com]
if triggercommand[:-1] == "+":
triggercommand = triggercommand[:-1]
if not triggercommand or not len(triggercommand):
return
if triggercommand.lower() not in list(commands_list.keys()):
dispmsg = ["Cannot find any alias commands: No valid commands match " + str(triggercommand) + "."]
closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse')
if len(closestmatches):
dispmsg.append("The following commands match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".")
bot.notice(dispmsg, trigger.nick)
return
realcom = triggercommand
if "aliasfor" in commands_list[triggercommand].keys():
realcom = commands_list[triggercommand]["aliasfor"]
validcomlist = commands_list[realcom]["validcoms"]
bot.notice("The following commands match " + str(triggercommand) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick)
return
if triggercommand[:-1] == "?":
triggercommand = triggercommand[:-1]
if not triggercommand or not len(triggercommand):
return
closestmatches = similar_list(bot, triggercommand, list(commands_list.keys()), 10, 'reverse')
if not len(closestmatches):
bot.notice("Cannot find any similar commands for " + str(triggercommand) + ".", trigger.nick)
else:
bot.notice("The following commands may match " + str(triggercommand) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick)
return
commandlist = []
for command in list(commands_list.keys()):
if command.lower().startswith(str(triggercommand).lower()):
commandlist.append(command)
if not len(commandlist):
bot.notice("No commands start with " + str(triggercommand) + ".", trigger.nick)
else:
bot.notice("The following commands start with " + str(triggercommand) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick)

View File

@ -1,14 +0,0 @@
# coding=utf8
"""Sopel Commands Query
Sopel Commands Query responds to ? to display commands matching patterns
"""
from __future__ import unicode_literals, absolute_import, division, print_function
from .CommandsQuery import *
from .QueryCommand import *
from .NickQueryCommand import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>MY DOGS NAME IS *</pattern>
<template>
That is interesting that you have a dog named <set name="dog"><star/></set>
</template>
</category>
<category>
<pattern>WHAT IS MY DOGS NAME</pattern>
<template>
Your dog's name is <get name="dog"/>.
</template>
</category>
</aiml>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICKexclamationmark</pattern>
<template>triggernick!</template>
</category>
<category>
<pattern>BOTNICKquestionmark</pattern>
<template>I'm here, triggernick</template>
</category>
<category>
<pattern>BOTNICK</pattern>
<template>
<random>
<li>That's my name, don't wear it out!</li>
<li>Need something, triggernick?</li>
<li>What's going on, triggernick?</li>
</random>
</template>
</category>
</aiml>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK GENDER</pattern>
<template>If societal genders are binary, my gender is <bot name="gender"/></template>
</category>
<category>
<pattern>BOTNICK WHAT IS YOUR GENDER</pattern>
<template>If societal genders are binary, my gender is <bot name="gender"/></template>
</category>
</aiml>

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK BYE</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK GOODBYE</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK seeya</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK cya</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK ttyl</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BYE BOTNICK</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>GOODBYE BOTNICK</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>seeya BOTNICK</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>cya BOTNICK</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>ttyl BOTNICK</pattern>
<template>
<random>
<li>bye, triggernick</li>
<li>goodbye, triggernick</li>
<li>seeya, triggernick</li>
<li>cya, triggernick</li>
<li>ttyl, triggernick</li>
</random>
</template>
</category>
</aiml>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK hi</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK hello</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK hey</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>hi BOTNICK</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>hello BOTNICK</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>hey BOTNICK</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
</aiml>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK fuck you</pattern>
<template>Watch your mouth, triggernick, or I'll tell your mother!</template>
</category>
<category>
<pattern>BOTNICK fuck off</pattern>
<template>Watch your mouth, triggernick, or I'll tell your mother!</template>
</category>
<category>
<pattern>BOTNICK screw you</pattern>
<template>Watch your mouth, triggernick, or I'll tell your mother!</template>
</category>
<category>
<pattern>fuck you BOTNICK</pattern>
<template>Watch your mouth, triggernick, or I'll tell your mother!</template>
</category>
<category>
<pattern>fuck off BOTNICK</pattern>
<template>Watch your mouth, triggernick, or I'll tell your mother!</template>
</category>
<category>
<pattern>screw you BOTNICK</pattern>
<template>Watch your mouth, triggernick, or I'll tell your mother!</template>
</category>
</aiml>

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK IS LAZY</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>LAZY BOTNICK</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
<category>
<pattern>DAMNIT BOTNICK</pattern>
<template>
<random>
<li>hi, triggernick</li>
<li>hello, triggernick</li>
<li>hey, triggernick</li>
</random>
</template>
</category>
</aiml>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK I love you</pattern>
<template>triggernick, I love you too.</template>
</category>
<category>
<pattern>BOTNICK loveya</pattern>
<template>triggernick, I love you too.</template>
</category>
<category>
<pattern>BOTNICK I love you</pattern>
<template>triggernick, I love you too.</template>
</category>
<category>
<pattern>BOTNICK I like you</pattern>
<template>triggernick, I like you too.</template>
</category>
<category>
<pattern>I love you BOTNICK</pattern>
<template>triggernick, I love you too.</template>
</category>
<category>
<pattern>loveya BOTNICK</pattern>
<template>triggernick, I love you too.</template>
</category>
<category>
<pattern>I love BOTNICK</pattern>
<template>triggernick, I love you too.</template>
</category>
<category>
<pattern>I like BOTNICK</pattern>
<template>triggernick, I like you too.</template>
</category>
</aiml>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK PING</pattern>
<template>pong</template>
</category>
</aiml>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK IS COOL</pattern>
<template>Thanks!</template>
</category>
</aiml>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK really</pattern>
<template>
<random>
<li>triggernick, Yes, really.</li>
<li>triggernick, Really really</li>
</random>
</template>
</category>
<category>
<pattern>really BOTNICK</pattern>
<template>
<random>
<li>triggernick, Yes, really.</li>
<li>triggernick, Really really</li>
</random>
</template>
</category>
</aiml>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>thank you BOTNICK</pattern>
<template>triggernick, I like you too.</template>
</category>
<category>
<pattern>BOTNICK thank you</pattern>
<template>triggernick, I like you too.</template>
</category>
<category>
<pattern>thanks BOTNICK</pattern>
<template>triggernick, I like you too.</template>
</category>
<category>
<pattern>BOTNICK thanks</pattern>
<template>triggernick, I like you too.</template>
</category>
<category>
<pattern>thankya BOTNICK</pattern>
<template>triggernick, I like you too.</template>
</category>
<category>
<pattern>BOTNICK thankya</pattern>
<template>triggernick, I like you too.</template>
</category>
</aiml>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK welcome back</pattern>
<template>Thanks!</template>
</category>
<category>
<pattern>welcome back BOTNICK</pattern>
<template>Thanks!</template>
</category>
</aiml>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK what is</pattern>
<template>Do you think this is Jeopardy?</template>
</category>
</aiml>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<aiml version="1.0">
<category>
<pattern>BOTNICK where is waldo</pattern>
<template>He is hiding for a reason?</template>
</category>
<category>
<pattern>BOTNICK where is wally</pattern>
<template>He is hiding for a reason?</template>
</category>
<category>
<pattern>BOTNICK where is carmen sandiego</pattern>
<template>
<random>
<li>Currently she is located at ACME Headquarters</li>
<li>Currently she is located at Villains' International League of Evil</li>
</random>
</template>
</category>
<category>
<pattern>BOTNICK where is</pattern>
<template>Not sure what you want me to look for.</template>
</category>
</aiml>

View File

@ -1,28 +1,26 @@
# coding=utf-8
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
"""
This is the SpiceBot Channels system.
"""
import sopel
import spicemanip
from sopel_modules.SpiceBot.Events import botevents
from sopel_modules.SpiceBot.Channels import botchannels
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, inlist, channel_privs
import sopel_modules.SpiceBot as SpiceBot
@botevents.check_ready([botevents.BOT_LOADED])
@SpiceBot.events.check_ready([SpiceBot.events.BOT_LOADED])
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('channels', 'channel')
def nickname_comand_channels(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command')
if not len(triggerargs):
if not len(trigger.sb['args']):
commandused = 'list'
else:
commandused = spicemanip.main(triggerargs, 1).lower()
commandused = spicemanip.main(trigger.sb['args'], 1).lower()
triggerargs = spicemanip.main(triggerargs, '2+', 'list')
trigger.sb['args'] = spicemanip.main(trigger.sb['args'], '2+', 'list')
if commandused == 'list':
chanlist = spicemanip.main(bot.channels.keys(), 'andlist')
@ -31,14 +29,14 @@ def nickname_comand_channels(bot, trigger):
elif commandused == 'total':
botcount = len(bot.channels.keys())
servercount = len(botchannels.SpiceBot_Channels['list'].keys())
servercount = len(SpiceBot.channels.dict['list'].keys())
bot.osd("I am in " + str(botcount) + " of " + str(servercount) + " channel(s) available on this server.")
return
elif commandused == 'random':
channel = spicemanip.main(botchannels.SpiceBot_Channels['list'], 'random')
topic = botchannels.SpiceBot_Channels['list'][channel]['topic']
msg = ["Random channel for you: {}.".format(botchannels.SpiceBot_Channels['list'][channel]['name'])]
channel = spicemanip.main(SpiceBot.channels.dict['list'], 'random')
topic = SpiceBot.channels.dict['list'][channel]['topic']
msg = ["Random channel for you: {}.".format(SpiceBot.channels.dict['list'][channel]['name'])]
if topic and not topic.isspace():
msg.append("The topic is: {}".format(topic))
else:
@ -50,47 +48,47 @@ def nickname_comand_channels(bot, trigger):
if not trigger.admin:
bot.osd("You do not have permission to update the channel listing.")
return
botchannels.bot_part_empty(bot)
botchannels.channel_list_request(bot)
SpiceBot.channels.bot_part_empty(bot)
SpiceBot.channels.channel_list_request(bot)
bot.osd(["[SpiceBot_Channels]", "I am now updating the channel listing for this server."])
while botchannels.channel_lock:
while SpiceBot.channels.channel_lock:
pass
botchannels.join_all_channels(bot)
foundchannelcount = len(botchannels.SpiceBot_Channels['list'].keys())
SpiceBot.channels.join_all_channels(bot)
foundchannelcount = len(SpiceBot.channels.dict['list'].keys())
bot.osd("[SpiceBot_Channels]", "Channel listing finished!", str(foundchannelcount) + " channel(s) found.")
botchannels.bot_part_empty(bot)
SpiceBot.channels.bot_part_empty(bot)
return
elif commandused == 'topic':
if not len(triggerargs):
if not len(trigger.sb['args']):
bot.osd("Channel name input missing.")
return
channel = spicemanip.main(triggerargs, 1)
if not inlist(bot, channel.lower(), botchannels.SpiceBot_Channels['list'].keys()):
channel = spicemanip.main(trigger.sb['args'], 1)
if not SpiceBot.inlist(channel.lower(), SpiceBot.channels.dict['list'].keys()):
bot.osd("Channel name {} not valid.".format(channel))
return
topic = botchannels.SpiceBot_Channels['list'][channel.lower()]['topic']
channel = botchannels.SpiceBot_Channels['list'][channel.lower()]['name']
topic = SpiceBot.channels.dict['list'][channel.lower()]['topic']
channel = SpiceBot.channels.dict['list'][channel.lower()]['name']
bot.osd("Topic for {}: {}".format(channel, topic))
return
if commandused.upper() in ['OP', 'HOP', 'VOICE', 'OWNER', 'ADMIN']:
if not len(triggerargs):
if not len(trigger.sb['args']):
if trigger.is_privmsg:
bot.osd("Channel name required.")
return
else:
channel = trigger.sender
else:
channel = spicemanip.main(triggerargs, 1)
if not inlist(bot, channel.lower(), botchannels.SpiceBot_Channels['list'].keys()):
channel = spicemanip.main(trigger.sb['args'], 1)
if not SpiceBot.inlist(channel.lower(), SpiceBot.channels.dict['list'].keys()):
bot.osd("Channel name {} not valid.".format(channel))
return
if not inlist(bot, channel.lower(), bot.channels.keys()):
if not SpiceBot.inlist(channel.lower(), bot.channels.keys()):
bot.osd("I need to be in {} to see nick privileges.".format(channel))
return
privlist = channel_privs(bot, channel, commandused.upper())
privlist = SpiceBot.channel_privs(bot, channel, commandused.upper())
dispmsg = []
if not len(privlist):
dispmsg.append("There are no Channel " + commandused.upper() + "s for " + str(channel))
@ -102,11 +100,11 @@ def nickname_comand_channels(bot, trigger):
# Users List
if commandused == 'users':
channel = spicemanip.main(triggerargs, 1)
if not inlist(bot, channel.lower(), botchannels.SpiceBot_Channels['list'].keys()):
channel = spicemanip.main(trigger.sb['args'], 1)
if not SpiceBot.inlist(channel.lower(), SpiceBot.channels.dict['list'].keys()):
bot.osd("Channel name {} not valid.".format(channel))
return
if not inlist(bot, channel.lower(), bot.channels.keys()):
if not SpiceBot.inlist(channel.lower(), bot.channels.keys()):
bot.osd("I need to be in {} to see user list.".format(channel))
return
dispmsg = []

View File

@ -0,0 +1,175 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Commands system.
This Class stores commands in an easy to access manner
"""
import sopel
import spicemanip
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.events.check_ready([SpiceBot.events.BOT_COMMANDS])
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('commands', 'command')
def nickname_comand_channels(bot, trigger):
if not len(trigger.sb['args']):
commandused = 'list'
else:
commandused = spicemanip.main(trigger.sb['args'], 1).lower()
commands_list = []
for commandstype in SpiceBot.commands.dict['commands'].keys():
if commandstype not in ['rule']:
for com in SpiceBot.commands.dict['commands'][commandstype].keys():
if com not in commands_list:
if commandstype in ['nickname']:
commands_list.append(bot.nick + " " + com)
else:
commands_list.append(com)
if commandused == 'list':
bot.osd(spicemanip.main(commands_list, 'andlist'), trigger.nick, 'NOTICE')
return
elif commandused == 'total':
bot.osd("I have " + str(len(commands_list)) + " commands available.")
return
elif commandused == 'random':
bot.osd(["Here's a random command for you:", + spicemanip.main(commands_list, 'random')])
return
@SpiceBot.events.check_ready([SpiceBot.events.BOT_COMMANDS])
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('(.*)')
def query_detection_nick(bot, trigger):
# command issued, check if valid
if not trigger.sb['com'] or not len(trigger.sb['com']):
return
if not trigger.sb['com'][0] == "?":
return
trigger.sb['com'] = trigger.sb['com'][1:]
if not SpiceBot.letters_in_string(trigger.sb['com']):
return
commands_list = dict()
for com in SpiceBot.commands.dict['commands']['nickname'].keys():
if com not in commands_list.keys():
commands_list[com] = SpiceBot.commands.dict['commands']['nickname'][com]
if trigger.sb['com'].endswith("+"):
trigger.sb['com'] = trigger.sb['com'][:-1]
if not trigger.sb['com'] or not len(trigger.sb['com']):
return
if trigger.sb['com'].lower() not in list(commands_list.keys()):
dispmsg = ["Cannot find any alias " + bot.nick + " commands: No valid commands match " + str(trigger.sb['com']) + "."]
closestmatches = SpiceBot.similar_list(trigger.sb['com'], list(commands_list.keys()), 10, 'reverse')
if len(closestmatches):
dispmsg.append("The following " + bot.nick + " commands match " + str(trigger.sb['com']) + ": " + spicemanip.main(closestmatches, 'andlist') + ".")
bot.osd(dispmsg, trigger.nick, 'notice')
return
realcom = trigger.sb['com']
if "aliasfor" in commands_list[trigger.sb['com']].keys():
realcom = commands_list[trigger.sb['com']]["aliasfor"]
validcomlist = commands_list[realcom]["validcoms"]
bot.osd("The following " + bot.nick + " commands match " + str(trigger.sb['com']) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick, 'notice')
return
if trigger.sb['com'].endswith("?"):
trigger.sb['com'] = trigger.sb['com'][:-1]
if not trigger.sb['com'] or not len(trigger.sb['com']):
return
closestmatches = SpiceBot.similar_list(trigger.sb['com'], list(commands_list.keys()), 10, 'reverse')
if not len(closestmatches):
bot.osd("Cannot find any similar " + bot.nick + " commands for " + str(trigger.sb['com']) + ".", trigger.nick, 'notice')
else:
bot.osd("The following " + bot.nick + " commands may match " + str(trigger.sb['com']) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick, 'notice')
return
commandlist = []
for command in list(commands_list.keys()):
if command.lower().startswith(str(trigger.sb['com']).lower()):
commandlist.append(command)
if not len(commandlist):
bot.osd("No " + bot.nick + " commands start with " + str(trigger.sb['com']) + ".", trigger.nick, 'notice')
else:
bot.osd("The following " + bot.nick + " commands start with " + str(trigger.sb['com']) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick, 'notice')
@SpiceBot.events.check_ready([SpiceBot.events.BOT_COMMANDS])
@SpiceBot.prerun.prerun('query')
@sopel.module.rule('^\?(.*)')
def query_detection(bot, trigger):
# command issued, check if valid
if not trigger.sb['com'] or not len(trigger.sb['com']):
return
if not SpiceBot.letters_in_string(trigger.sb['com']):
return
commands_list = dict()
for commandstype in SpiceBot.commands.dict['commands'].keys():
if commandstype not in ['rule', 'nickname']:
for com in SpiceBot.commands.dict['commands'][commandstype].keys():
if com not in commands_list.keys():
commands_list[com] = SpiceBot.commands.dict['commands'][commandstype][com]
if trigger.sb['com'][:-1] == "+":
trigger.sb['com'] = trigger.sb['com'][:-1]
if not trigger.sb['com'] or not len(trigger.sb['com']):
return
if trigger.sb['com'].lower() not in list(commands_list.keys()):
dispmsg = ["Cannot find any alias commands: No valid commands match " + str(trigger.sb['com']) + "."]
closestmatches = SpiceBot.similar_list(trigger.sb['com'], list(commands_list.keys()), 10, 'reverse')
if len(closestmatches):
dispmsg.append("The following commands match " + str(trigger.sb['com']) + ": " + spicemanip.main(closestmatches, 'andlist') + ".")
bot.osd(dispmsg, trigger.nick, 'notice')
return
realcom = trigger.sb['com']
if "aliasfor" in commands_list[trigger.sb['com']].keys():
realcom = commands_list[trigger.sb['com']]["aliasfor"]
validcomlist = commands_list[realcom]["validcoms"]
bot.osd("The following commands match " + str(trigger.sb['com']) + ": " + spicemanip.main(validcomlist, 'andlist') + ".", trigger.nick, 'notice')
return
if trigger.sb['com'][:-1] == "?":
trigger.sb['com'] = trigger.sb['com'][:-1]
if not trigger.sb['com'] or not len(trigger.sb['com']):
return
closestmatches = SpiceBot.similar_list(trigger.sb['com'], list(commands_list.keys()), 10, 'reverse')
if not len(closestmatches):
bot.osd("Cannot find any similar commands for " + str(trigger.sb['com']) + ".", trigger.nick, 'notice')
else:
bot.osd("The following commands may match " + str(trigger.sb['com']) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick, 'notice')
return
commandlist = []
for command in list(commands_list.keys()):
if command.lower().startswith(str(trigger.sb['com']).lower()):
commandlist.append(command)
if not len(commandlist):
bot.osd("No commands start with " + str(trigger.sb['com']) + ".", trigger.nick, 'notice')
else:
bot.osd("The following commands start with " + str(trigger.sb['com']) + ": " + spicemanip.main(commandlist, 'andlist') + ".", trigger.nick, 'notice')

View File

@ -0,0 +1,15 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Database
"""
# sopel imports
import sopel.module
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.nickname_commands('database')
def bot_command_database(bot, trigger):
bot.osd("Database is " + SpiceBot.config.db_type)

View File

@ -0,0 +1,16 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.nickname_commands('help', 'docs', 'wiki')
def bot_command_owners(bot, trigger):
bot.osd(["IRC Modules Repository",
str(SpiceBot.github_dict["url_main"] +
SpiceBot.github_dict["repo_owner"] + "/" +
SpiceBot.github_dict["repo_name"] +
SpiceBot.github_dict["url_path_wiki"])])

View File

@ -0,0 +1,49 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Logs System
"""
# sopel imports
import sopel.module
import spicemanip
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.events.check_ready([SpiceBot.events.BOT_LOGS])
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('logs', 'debug')
def bot_command_logs(bot, trigger):
if not SpiceBot.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
bot.osd("I was unable to process this Bot Nick command due to privilege issues.")
return
logtype = spicemanip.main(trigger.sb['args'], 1) or None
if not logtype:
bot.osd("Current valid log(s) include: " + spicemanip.main(SpiceBot.logs.dict["list"].keys(), 'andlist'), trigger.sender, 'action')
return
if not SpiceBot.inlist(logtype, SpiceBot.logs.dict["list"].keys()):
closestmatches = SpiceBot.similar_list(logtype, SpiceBot.logs.dict["list"].keys(), 10, 'reverse')
if not len(closestmatches):
bot.osd("No valid logs match " + str(logtype) + ".", trigger.nick, 'notice')
else:
bot.osd("The following commands may match " + str(logtype) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick, 'notice')
return
logtype = SpiceBot.inlist_match(logtype, SpiceBot.logs.dict["list"].keys())
logindex = SpiceBot.logs.get_logs(logtype)
if not len(logindex):
bot.osd("No logs found for " + str(logtype) + ".")
return
bot.osd("Is Examining " + str(logtype) + " log(s).")
for line in logindex:
bot.osd(" " + str(line))
bot.osd(str(logtype) + " log(s) Complete.")

View File

@ -6,20 +6,22 @@ import sopel.module
import spicemanip
from sopel_modules.SpiceBot.Channels import botchannels
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, command_permissions_check, inlist
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('action')
def bot_command_action(bot, trigger):
bot_command_process(bot, trigger)
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('notice')
def bot_command_notice(bot, trigger):
bot_command_process(bot, trigger)
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('privmsg', 'say', 'msg')
def bot_command_privmsg(bot, trigger):
bot_command_process(bot, trigger)
@ -27,27 +29,25 @@ def bot_command_privmsg(bot, trigger):
def bot_command_process(bot, trigger):
if not command_permissions_check(bot, trigger, ['admins', 'owner']):
bot.say("I was unable to process this Bot Nick command due to privilege issues.")
if not SpiceBot.command_permissions_check(bot, trigger, ['admins', 'owner']):
bot.osd("I was unable to process this Bot Nick command due to privilege issues.")
return
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command')
if trigger.sb['com'] in ['say', 'msg']:
trigger.sb['com'] = 'privmsg'
if triggercommand in ['say', 'msg']:
triggercommand = 'privmsg'
if not len(triggerargs):
bot.say("You must specify a channel or nick.")
if not len(trigger.sb['args']):
bot.osd("You must specify a channel or nick.")
return
target = spicemanip.main(triggerargs, 1)
target = spicemanip.main(trigger.sb['args'], 1)
if (target not in ['allchans', 'allnicks']
and not inlist(bot, target.lower(), botchannels.SpiceBot_Channels['list'].keys())
and not inlist(bot, target.lower(), bot.users)):
and not SpiceBot.inlist(target.lower(), SpiceBot.channels.dict['list'].keys())
and not SpiceBot.inlist(target.lower(), bot.users)):
bot.osd("Channel/nick name {} not valid.".format(target))
return
triggerargs = spicemanip.main(triggerargs, '2+', 'list')
trigger.sb['args'] = spicemanip.main(trigger.sb['args'], '2+', 'list')
if target == 'allchans':
targetsendlist = bot.channels.keys()
@ -56,9 +56,9 @@ def bot_command_process(bot, trigger):
else:
targetsendlist = [target]
botmessage = spicemanip.main(triggerargs, 0)
botmessage = spicemanip.main(trigger.sb['args'], 0)
if not botmessage:
bot.say("You must specify a message to send.")
bot.osd("You must specify a message to send.")
return
bot.osd(botmessage, targetsendlist, triggercommand.upper())
bot.osd(botmessage, targetsendlist, trigger.sb['com'].upper())

View File

@ -0,0 +1,55 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import spicemanip
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.prerun.prerun('nickname')
@sopel.module.nickname_commands('update')
def nickname_comand_update(bot, trigger):
if not SpiceBot.command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
bot.osd("I was unable to process this Bot Nick command due to privilege issues.")
return
if not len(trigger.sb['args']):
commandused = 'nodeps'
else:
commandused = spicemanip.main(trigger.sb['args'], 1).lower()
if commandused not in ['deps', 'nodeps']:
bot.osd("Please specify deps or nodeps")
return
trigger.sb['args'] = spicemanip.main(trigger.sb['args'], '2+', 'list')
quitmessage = "Received command from " + trigger.nick + " to update from Github and restart"
SpiceBot.logs.log('SpiceBot_Update', quitmessage)
bot.osd(quitmessage, bot.channels.keys())
if commandused == 'nodeps':
SpiceBot.spicebot_update(False)
if commandused == 'deps':
SpiceBot.spicebot_update(True)
# service_manip(bot.nick, 'restart', 'SpiceBot_Update')
SpiceBot.spicebot_reload(bot, 'SpiceBot_Update', quitmessage)
@sopel.module.nickname_commands('restart')
def nickname_comand_restart(bot, trigger):
if not trigger.admin:
bot.osd("You are not authorized to perform this function.")
quitmessage = "Received command from " + trigger.nick + " to restart. Be Back Soon!"
SpiceBot.logs.log('SpiceBot_Restart', quitmessage)
bot.osd(quitmessage, bot.channels.keys())
# service_manip(bot.nick, 'restart', 'SpiceBot_Restart')
SpiceBot.spicebot_reload(bot, 'SpiceBot_Restart', quitmessage)

View File

@ -0,0 +1,15 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import time
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.nickname_commands('uptime')
def bot_command_srewyou(bot, trigger):
timesince = str(SpiceBot.humanized_time(time.time() - SpiceBot.events.BOT_UPTIME)) + " ago."
bot.osd("I have been running since " + timesince)

View File

@ -0,0 +1,20 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
These are the core SpiceBot Classes
This module contains references only for other modules to utilize.
"""
from .Commands import *
from .Channels import *
from .Database import *
from .Uptime import *
from .Logs import *
from .Runtime_Controls import *
from .Messaging import *
from .Docs import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -0,0 +1,17 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot AI system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.BOT_READY)
@sopel.module.rule('.*')
def bot_events_complete_ai(bot, trigger):
SpiceBot.logs.log('SpiceBot_Commands', "Found " + str(SpiceBot.botai.dict['patterncounts']) + " AI pattern Matches.", True)
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_AI, "SpiceBot_AI")

View File

@ -0,0 +1,97 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Channels system.
"""
import sopel
import time
import spicemanip
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.RPL_WELCOME)
@sopel.module.rule('.*')
def unkickable_bot(bot, trigger):
if SpiceBot.config.SpiceBot_Channels.operadmin:
bot.write(('SAMODE', bot.nick, '+q'))
@sopel.module.event(SpiceBot.events.RPL_WELCOME)
@sopel.module.rule('.*')
def request_channels_list_initial(bot, trigger):
SpiceBot.channels.bot_part_empty(bot)
SpiceBot.logs.log('SpiceBot_Channels', "Sending Request for all server channels")
SpiceBot.channels.channel_list_request(bot)
starttime = time.time()
# wait 60 seconds for initial population of information
while not SpiceBot.channels.dict['InitialProcess']:
if time.time() - starttime >= 60:
SpiceBot.logs.log('SpiceBot_Channels', "Initial Channel list populating Timed Out")
SpiceBot.channels.dict['InitialProcess'] = True
else:
pass
foundchannelcount = len(SpiceBot.channels.dict['list'].keys())
SpiceBot.logs.log('SpiceBot_Channels', "Channel listing finished! " + str(foundchannelcount) + " channel(s) found.")
SpiceBot.channels.join_all_channels(bot)
SpiceBot.channels.chanadmin_all_channels(bot)
if "*" in SpiceBot.channels.dict['list'].keys():
del SpiceBot.channels.dict['list']["*"]
SpiceBot.channels.bot_part_empty(bot)
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_CHANNELS, "SpiceBot_Channels")
@sopel.module.event('321')
@sopel.module.rule('.*')
def watch_chanlist_start(bot, trigger):
SpiceBot.channels.channel_list_recieve_start()
@sopel.module.event('322')
@sopel.module.rule('.*')
def watch_chanlist_populate(bot, trigger):
SpiceBot.channels.channel_list_recieve_input(trigger)
@sopel.module.event('323')
@sopel.module.rule('.*')
def watch_chanlist_complete(bot, trigger):
SpiceBot.channels.channel_list_recieve_finish()
@sopel.module.event(SpiceBot.events.BOT_CHANNELS)
@sopel.module.rule('.*')
def trigger_channel_list_recurring(bot, trigger):
while True:
time.sleep(1800)
SpiceBot.channels.bot_part_empty(bot)
oldlist = list(SpiceBot.channels.dict['list'].keys())
SpiceBot.channels.channel_list_request(bot)
while SpiceBot.channels.channel_lock:
pass
newlist = [item.lower() for item in oldlist if item.lower() not in list(SpiceBot.channels.dict['list'].keys())]
if "*" in newlist:
newlist.remove("*")
if len(newlist) and SpiceBot.config.SpiceBot_Channels.announcenew:
bot.osd(["The Following channel(s) are new:", spicemanip.main(newlist, 'andlist')], bot.channels.keys())
SpiceBot.channels.join_all_channels(bot)
SpiceBot.channels.chanadmin_all_channels(bot)
if "*" in SpiceBot.channels.dict['list'].keys():
del SpiceBot.channels.dict['list']["*"]
SpiceBot.channels.bot_part_empty(bot)

View File

@ -0,0 +1,54 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Events system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.BOT_WELCOME, SpiceBot.events.BOT_READY, SpiceBot.events.BOT_CONNECTED, SpiceBot.events.BOT_LOADED)
@sopel.module.rule('.*')
def bot_events_complete(bot, trigger):
"""This is here simply to log to stderr that this was recieved."""
SpiceBot.logs.log('SpiceBot_Events', trigger.args[1], True)
@sopel.module.event(SpiceBot.events.RPL_WELCOME)
@sopel.module.rule('.*')
def bot_events_connected(bot, trigger):
# Handling for connection count
SpiceBot.events.dict["RPL_WELCOME_Count"] += 1
if SpiceBot.events.dict["RPL_WELCOME_Count"] > 1:
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_RECONNECTED, "Bot ReConnected to IRC")
else:
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_WELCOME, "Welcome to the SpiceBot Events System")
"""For items tossed in a queue, this will trigger them accordingly"""
while True:
if len(SpiceBot.events.dict["trigger_queue"]):
pretriggerdict = SpiceBot.events.dict["trigger_queue"][0]
SpiceBot.events.dispatch(bot, pretriggerdict)
del SpiceBot.events.dict["trigger_queue"][0]
@sopel.module.event(SpiceBot.events.BOT_WELCOME)
@sopel.module.rule('.*')
def bot_events_start(bot, trigger):
"""This stage is redundant, but shows the system is working."""
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_READY, "Ready To Process module setup procedures")
"""Here, we wait until we are in at least one channel"""
while not len(bot.channels.keys()) > 0:
pass
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_CONNECTED, "Bot Connected to IRC")
@SpiceBot.events.startup_check_ready()
@sopel.module.event(SpiceBot.events.BOT_READY)
@sopel.module.rule('.*')
def bot_events_startup_complete(bot, trigger):
"""All events registered as required for startup have completed"""
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_LOADED, "All registered modules setup procedures have completed")

View File

@ -0,0 +1,28 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot OSD system.
"""
import sopel
@sopel.module.event('005')
@sopel.module.rule('.*')
def parse_event_005(bot, trigger):
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]
for param in parameters:
if '=' in param:
if param.startswith("TARGMAX"):
param = str(param).split('=')[1]
settings = str(param).split(',')
for setting in settings:
settingname = str(setting).split(':')[0]
if settingname.upper() in ['KICK']:
try:
value = str(setting).split(':')[1] or None
except IndexError:
value = None
if value:
bot.config.SpiceBot_Kick.kick = int(value)

View File

@ -0,0 +1,27 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Logs system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.RPL_WELCOME)
@sopel.module.rule('.*')
def join_log_channel(bot, trigger):
if SpiceBot.config.logging_channel:
channel = bot.config.core.logging_channel
if channel not in bot.channels.keys():
bot.write(('JOIN', bot.nick, channel))
if channel not in bot.channels.keys() and bot.config.SpiceBot_Channels.operadmin:
bot.write(('SAJOIN', bot.nick, channel))
while True:
if len(SpiceBot.logs.dict["queue"]):
bot.osd(str(SpiceBot.logs.dict["queue"][0]), channel)
del SpiceBot.logs.dict["queue"][0]
else:
SpiceBot.logs.dict["queue"] = []

View File

@ -0,0 +1,19 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Modules system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.BOT_READY)
@sopel.module.rule('.*')
def bot_events_complete_modules(bot, trigger):
for comtype in SpiceBot.commands.dict['commands'].keys():
if comtype not in ['module', 'nickname', 'rule']:
SpiceBot.logs.log('SpiceBot_Commands', "Found " + str(len(SpiceBot.commands.dict['commands'][comtype].keys())) + " " + comtype + " commands.", True)
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_COMMANDS, "SpiceBot_Commands")

View File

@ -0,0 +1,93 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot StartupMonologue system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
import time
@sopel.module.event(SpiceBot.events.BOT_CONNECTED)
@sopel.module.rule('.*')
def bot_startup_monologue_start(bot, trigger):
# Startup
SpiceBot.logs.log('SpiceBot_StartupMonologue', bot.nick + " is now starting. Please wait while I load my configuration")
bot.osd(" is now starting. Please wait while I load my configuration.", bot.channels.keys(), 'ACTION')
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_STARTUPMONOLOGUE_CONNECTED, "SpiceBot_StartupMonologue")
@sopel.module.event(SpiceBot.events.BOT_CHANNELS)
@sopel.module.rule('.*')
def bot_startup_monologue_channels(bot, trigger):
botcount = len(bot.channels.keys())
servercount = len(SpiceBot.channels.dict['list'].keys())
displayval = "I am in " + str(botcount) + " of " + str(servercount) + " channel(s) available on this server."
SpiceBot.startupmonologue.dict["channels"] = displayval
SpiceBot.logs.log('SpiceBot_StartupMonologue', displayval)
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_STARTUPMONOLOGUE_CHANNELS, "SpiceBot_StartupMonologue")
@sopel.module.event(SpiceBot.events.BOT_COMMANDS)
@sopel.module.rule('.*')
def bot_startup_monologue_commands(bot, trigger):
availablecomsnum, availablecomsfiles = 0, 0
for commandstype in SpiceBot.commands.dict['commands'].keys():
availablecomsnum += len(SpiceBot.commands.dict['commands'][commandstype].keys())
availablecomsfiles += SpiceBot.commands.dict['counts']
displayval = "There are " + str(availablecomsnum) + " commands available in " + str(availablecomsfiles) + " files."
SpiceBot.startupmonologue.dict["commands"] = displayval
SpiceBot.logs.log('SpiceBot_StartupMonologue', displayval)
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_STARTUPMONOLOGUE_COMMANDS, "SpiceBot_StartupMonologue")
@sopel.module.event(SpiceBot.events.BOT_AI)
@sopel.module.rule('.*')
def bot_startup_monologue_ai(bot, trigger):
availablecomsnum = SpiceBot.botai.dict['patterncounts']
availablecomsfiles = SpiceBot.botai.dict['filecounts']
displayval = "There are " + str(availablecomsnum) + " AI pattern matches available in " + str(availablecomsfiles) + " files."
SpiceBot.startupmonologue.dict["ai"] = displayval
SpiceBot.logs.log('SpiceBot_StartupMonologue', displayval)
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_STARTUPMONOLOGUE_AI, "SpiceBot_StartupMonologue")
@sopel.module.event(SpiceBot.events.BOT_LOADED)
@sopel.module.rule('.*')
def bot_startup_monologue_display(bot, trigger):
dispmsg = [" startup complete"]
for messagekey in SpiceBot.startupmonologue.dict.keys():
dispmsg.append(SpiceBot.startupmonologue.dict[messagekey])
if SpiceBot.events.dict["RPL_WELCOME_Count"] == 1:
timesince = SpiceBot.humanized_time(time.time() - SpiceBot.events.BOT_UPTIME)
if timesince == "just now":
timesince = "1 second"
dispmsg.append("Startup took " + timesince)
# Announce to chan, then handle some closing stuff
SpiceBot.logs.log('SpiceBot_StartupMonologue', bot.nick + " startup complete")
bot.osd(dispmsg, bot.channels.keys(), 'ACTION')
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_STARTUPMONOLOGUE, "SpiceBot_StartupMonologue")
SpiceBot.logs.log('SpiceBot_StartupMonologue', "Startup Monologue has been issued to all channels.", True)
@sopel.module.event(SpiceBot.events.BOT_STARTUPMONOLOGUE)
@sopel.module.rule('.*')
def bot_startup_monologue_errors(bot, trigger):
debuglines = SpiceBot.logs.stdio_logs_fetch()
searchphrasefound = []
for line in debuglines:
if str(line).endswith("failed to load") and not str(line).startswith("0"):
searchphrasefound.append(line)
if len(searchphrasefound):
for foundphase in searchphrasefound:
SpiceBot.logs.log('SpiceBot_Logs', str(foundphase))
searchphrasefound.insert(0, "Notice to Bot Admins: ")
searchphrasefound.append("Run the debug command for more information.")
bot.osd(searchphrasefound, bot.channels.keys())
else:
SpiceBot.logs.log('SpiceBot_Logs', "No issues found at bot startup!", True)

View File

@ -0,0 +1,21 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
These are the core SpiceBot Classes
This module contains references only for other modules to utilize.
"""
from .Channels import *
from .Events import *
from .Modules import *
from .AI import *
from .osd import *
from .StartupMonologue import *
from .Logs import *
from .Kick import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -0,0 +1,31 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot OSD system.
"""
import sopel
@sopel.module.event('005')
@sopel.module.rule('.*')
def parse_event_005(bot, trigger):
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]
for param in parameters:
if '=' in param:
if param.startswith("TARGMAX"):
param = str(param).split('=')[1]
settings = str(param).split(',')
for setting in settings:
settingname = str(setting).split(':')[0]
if settingname.upper() in ['NOTICE', 'PRIVMSG']:
try:
value = str(setting).split(':')[1] or None
except IndexError:
value = None
if value:
if settingname.upper() == 'NOTICE':
bot.config.SpiceBot_OSD.notice = int(value)
elif settingname.upper() == 'PRIVMSG':
bot.config.SpiceBot_OSD.privmsg = int(value)

View File

@ -1,120 +0,0 @@
#!/usr/bin/env python
# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function, division
# sopel imports
import sopel.module
from sopel.tools import Identifier
from sopel.db import SopelDB, NickValues, ChannelValues
from sqlalchemy.exc import SQLAlchemyError
import json
from sopel_modules.SpiceBot.Logs import botlogs
def setup(bot):
pass
# Inject Database Functions
botlogs.log('SpiceBot_Databaseaddons', "Implanting Database functions into bot")
SopelDB.delete_nick_value = SopelDBCache.delete_nick_value
SopelDB.adjust_nick_value = SopelDBCache.adjust_nick_value
SopelDB.delete_channel_value = SopelDBCache.delete_channel_value
SopelDB.adjust_channel_value = SopelDBCache.adjust_channel_value
class SopelDBCache:
"""Nicks"""
def delete_nick_value(self, nick, key):
"""Deletes the value for a given key to be associated with the nick."""
nick = Identifier(nick)
nick_id = self.get_nick_id(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(NickValues.nick_id == nick_id) \
.filter(NickValues.key == key) \
.one_or_none()
# NickValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_nick_value(self, nick, key, value):
"""Sets the value for a given key to be associated with the nick."""
nick = Identifier(nick)
value = json.dumps(value, ensure_ascii=False)
nick_id = self.get_nick_id(nick)
session = self.ssession()
try:
result = session.query(NickValues) \
.filter(NickValues.nick_id == nick_id) \
.filter(NickValues.key == key) \
.one_or_none()
# NickValue exists, update
if result:
result.value = result.value + value
session.commit()
# DNE - Insert
else:
new_nickvalue = NickValues(nick_id=nick_id, key=key, value=value)
session.add(new_nickvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
"""Channels"""
def delete_channel_value(self, channel, key):
"""Sets the value for a given key to be associated with the channel."""
channel = Identifier(channel).lower()
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_channel_value(self, channel, key, value):
"""Sets the value for a given key to be associated with the channel."""
channel = Identifier(channel).lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ChannelValues) \
.filter(ChannelValues.channel == channel)\
.filter(ChannelValues.key == key) \
.one_or_none()
# ChannelValue exists, update
if result:
result.value = result.value + value
session.commit()
# DNE - Insert
else:
new_channelvalue = ChannelValues(channel=channel, key=key, value=value)
session.add(new_channelvalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()

View File

@ -1,6 +0,0 @@
# coding=utf-8
"""Useful miscellaneous tools and shortcuts for SpiceBot Sopel modules
"""
from __future__ import unicode_literals, absolute_import, print_function, division
from .Databaseaddons import *

View File

@ -1,19 +0,0 @@
#!/usr/bin/env python
# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function, division
# sopel imports
import sopel.module
from sopel_modules.SpiceBot.Logs import botlogs
def setup(bot):
botlogs.log('SpiceBot_DatabaseCache', "Setting up Database Cache")
if 'SpiceBot_DatabaseCache' not in bot.memory:
bot.memory['SpiceBot_DatabaseCache'] = dict()
def shutdown(bot):
if "SpiceBot_DatabaseCache" in bot.memory:
del bot.memory["SpiceBot_DatabaseCache"]

View File

@ -1,6 +0,0 @@
# coding=utf-8
"""Useful miscellaneous tools and shortcuts for SpiceBot Sopel modules
"""
from __future__ import unicode_literals, absolute_import, print_function, division
from .DatabaseCache import *

View File

@ -1,57 +0,0 @@
#!/usr/bin/env python
# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function, division
# sopel imports
import sopel.module
"""Database Direct"""
# Get a value
def db_get_nick_value(bot, nick, key):
database_value = bot.db.get_nick_value(nick, key) or None
return database_value
# set a value
def db_set_nick_value(bot, nick, key, value):
bot.db.set_nick_value(nick, key, value)
# DELETE a value
def db_reset_value(bot, nick, key):
bot.db.reset_nick_value(nick, key)
# add or subtract from current value
def db_adjust_value(bot, nick, key, value):
bot.db.adjust_nick_value(nick, key, value)
# list stored in database, add or remove elements
def db_adjust_list(bot, nick, entries, key, adjustmentdirection):
if not isinstance(entries, list):
entries = [entries]
adjustlist = db_get_nick_value(bot, nick, key) or []
adjustlistnew = []
for x in adjustlist:
adjustlistnew.append(x)
db_reset_value(bot, nick, key)
adjustlist = []
if adjustmentdirection == 'add':
for y in entries:
if y not in adjustlistnew:
adjustlistnew.append(y)
elif adjustmentdirection == 'del':
for y in entries:
if y in adjustlistnew:
adjustlistnew.remove(y)
for x in adjustlistnew:
if x not in adjustlist:
adjustlist.append(x)
if not len(adjustlist):
db_reset_value(bot, nick, key)
else:
db_set_nick_value(bot, nick, key, adjustlist)

View File

@ -1,6 +0,0 @@
# coding=utf-8
"""Useful miscellaneous tools and shortcuts for SpiceBot Sopel modules
"""
from __future__ import unicode_literals, absolute_import, print_function, division
from .DatabaseTools import *

View File

@ -1,12 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot_SBTools import github_dict
@sopel.module.nickname_commands('help', 'docs', 'wiki')
def bot_command_owners(bot, trigger):
bot.osd(["IRC Modules Repository", str(github_dict["url_main"] + github_dict["repo_owner"] + "/" + github_dict["repo_name"] + github_dict["url_path_wiki"])])

View File

@ -1,8 +0,0 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
from .Docs import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -1,16 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
@sopel.module.event(botevents.BOT_WELCOME)
@sopel.module.rule('.*')
def bot_events_Connected(bot, trigger):
"""Here, we wait until we are in at least one channel"""
while not len(bot.channels.keys()) > 0:
pass
botevents.trigger(bot, botevents.BOT_CONNECTED, "Bot Connected to IRC")

View File

@ -1,15 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
@sopel.module.event(botevents.RPL_WELCOME)
@sopel.module.rule('.*')
def bot_startup_welcome(bot, trigger):
botevents.SpiceBot_Events["RPL_WELCOME_Count"] += 1
if botevents.SpiceBot_Events["RPL_WELCOME_Count"] > 1:
botevents.trigger(bot, botevents.BOT_RECONNECTED, "Bot ReConnected to IRC")

View File

@ -1,15 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
@botevents.startup_check_ready()
@sopel.module.event(botevents.BOT_READY)
@sopel.module.rule('.*')
def bot_events_startup_complete(bot, trigger):
"""All events registered as required for startup have completed"""
botevents.trigger(bot, botevents.BOT_LOADED, "All registered modules setup procedures have completed")

View File

@ -1,15 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Logs import botlogs
from sopel_modules.SpiceBot.Events import botevents
@sopel.module.event(botevents.BOT_WELCOME, botevents.BOT_READY, botevents.BOT_CONNECTED, botevents.BOT_LOADED)
@sopel.module.rule('.*')
def bot_events_complete(bot, trigger):
"""This is here simply to log to stderr that this was recieved."""
botlogs.log('SpiceBot_Events', trigger.args[1], True)

View File

@ -1,18 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
@sopel.module.event(botevents.BOT_WELCOME)
@sopel.module.rule('.*')
def bot_events_connected(bot, trigger):
"""For items tossed in a queue, this will trigger them accordingly"""
while True:
if len(botevents.SpiceBot_Events["trigger_queue"]):
pretriggerdict = botevents.SpiceBot_Events["trigger_queue"][0]
botevents.dispatch(bot, pretriggerdict)
del botevents.SpiceBot_Events["trigger_queue"][0]

View File

@ -1,14 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
@sopel.module.event(botevents.BOT_WELCOME)
@sopel.module.rule('.*')
def bot_events_start(bot, trigger):
"""This stage is redundant, but shows the system is working."""
botevents.trigger(bot, botevents.BOT_READY, "Ready To Process module setup procedures")

View File

@ -1,17 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot.Events import botevents
@sopel.module.event(botevents.RPL_WELCOME)
@sopel.module.rule('.*')
def bot_startup_welcome(bot, trigger):
"""The Bot events system does not start until RPL_WELCOME 001 is recieved
from the server"""
if botevents.check(botevents.BOT_WELCOME):
return
botevents.trigger(bot, botevents.BOT_WELCOME, "Welcome to the SpiceBot Events System")

View File

@ -1,13 +0,0 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
from .Welcome import *
from .Ready import *
from .Connected import *
from .Loaded import *
from .Queue import *
from .Logging import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -1,10 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
@sopel.module.nickname_commands('gender')
def bot_command_gender(bot, trigger):
bot.osd("If societal genders are binary, my gender is female")

View File

@ -1,8 +0,0 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
from .Gender import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -8,24 +8,22 @@ import sopel.module
import spicemanip
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs
from sopel_modules.SpiceBot.Events import botevents
import sopel_modules.SpiceBot as SpiceBot
@botevents.check_ready([botevents.BOT_GIFSEARCH])
@SpiceBot.events.check_ready([SpiceBot.events.BOT_GIFSEARCH])
@SpiceBot.prerun.prerun('prefix')
@sopel.module.commands('(.*)')
def gifapi_triggers(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'prefix_command')
if triggercommand not in bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'].keys():
if trigger.sb['com'] not in bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'].keys():
return
if not len(triggerargs):
if not len(trigger.sb['args']):
return bot.osd("Please present a query to search.")
query = spicemanip.main(triggerargs, 0)
searchdict = {"query": query, "gifsearch": triggercommand}
query = spicemanip.main(trigger.sb['args'], 0)
searchdict = {"query": query, "gifsearch": trigger.sb['com']}
gifdict = getGif(bot, searchdict)

View File

@ -6,21 +6,20 @@ from sopel import module
from .gifsearch import getGif
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs
from sopel_modules.SpiceBot.Events import botevents
import sopel_modules.SpiceBot as SpiceBot
import spicemanip
@botevents.check_ready([botevents.BOT_GIFSEARCH])
@SpiceBot.events.check_ready([SpiceBot.events.BOT_GIFSEARCH])
@SpiceBot.prerun.prerun('prefix')
@module.commands('gif')
def gif_trigger(bot, trigger):
triggerargs, triggercommand = sopel_triggerargs(bot, trigger)
if not len(triggerargs):
if not len(trigger.sb['args']):
return bot.osd("Please present a query to search.")
query = spicemanip.main(triggerargs, 0)
query = spicemanip.main(trigger.sb['args'], 0)
searchapis = bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'].keys()
searchdict = {"query": query, "gifsearch": searchapis}

View File

@ -0,0 +1,33 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
from .gifsearch import getGif
import sopel.module
import spicemanip
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.events.check_ready([SpiceBot.events.BOT_GIFSEARCH])
@SpiceBot.prerun.prerun('prefix')
@sopel.module.commands('(.*)')
def gifapi_triggers(bot, trigger):
if trigger.sb['com'] not in bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'].keys():
return
if not len(trigger.sb['args']):
return bot.osd("Please present a query to search.")
query = spicemanip.main(trigger.sb['args'], 0)
searchdict = {"query": query, "gifsearch": trigger.sb['com']}
gifdict = getGif(bot, searchdict)
if gifdict["error"]:
bot.osd(gifdict["error"])
else:
bot.osd(str(gifdict['gifapi'].title() + " Result (" + str(query) + " #" + str(gifdict["returnnum"]) + "): " + str(gifdict["returnurl"])))

View File

@ -5,8 +5,9 @@ Sopel Gif Search builds a convenient framework for searching for Gifs
"""
from __future__ import unicode_literals, absolute_import, division, print_function
from .gif import *
from .gifapi import *
from .Gif_Command import *
from .Gif_API_Commands import *
# from .Gif_Search_Commands import *
from .gifsearch import *
__author__ = 'Sam Zick'

View File

@ -5,10 +5,7 @@ from __future__ import unicode_literals, absolute_import, division, print_functi
import sopel.module
from sopel.config.types import StaticSection, ValidatedAttribute
from sopel_modules.SpiceBot.Logs import botlogs
from sopel_modules.SpiceBot.Events import botevents
from sopel_modules.SpiceBot_CommandsQuery.CommandsQuery import commandsquery_register
from sopel_modules.SpiceBot_SBTools import read_directory_json_to_dict
import sopel_modules.SpiceBot as SpiceBot
import spicemanip
@ -32,7 +29,7 @@ class GifAPISection(StaticSection):
def configure(config):
moduledir = os.path.dirname(os.path.abspath(__file__))
api_dir = os.path.join(moduledir, 'gifapi')
valid_gif_api_dict = read_directory_json_to_dict(None, [api_dir], "Gif API", "SpiceBot_GifSearch")
valid_gif_api_dict = SpiceBot.read_directory_json_to_dict([api_dir], "Gif API", "SpiceBot_GifSearch", logging=True)
config.define_section("SopelGifSearch", GifAPIMainSection, validate=False)
config.SopelGifSearch.configure_setting('extra', 'SpiceBot_GifSearch API Extra directory')
@ -45,8 +42,8 @@ def configure(config):
def setup(bot):
botlogs.log('SpiceBot_GifSearch', "Starting Setup Procedure")
botevents.startup_add([botevents.BOT_GIFSEARCH])
SpiceBot.logs.log('SpiceBot_GifSearch', "Starting Setup Procedure")
SpiceBot.events.startup_add([SpiceBot.events.BOT_GIFSEARCH])
if 'SpiceBot_GifSearch' not in bot.memory:
bot.memory["SpiceBot_GifSearch"] = {"cache": {}, "badgiflinks": [], 'valid_gif_api_dict': {}}
@ -58,13 +55,15 @@ def setup(bot):
dir_to_scan.append(api_dir)
bot.config.define_section("SopelGifSearch", GifAPIMainSection, validate=False)
SpiceBot.config.define_section("SopelGifSearch", GifAPIMainSection, validate=False)
if bot.config.SopelGifSearch.extra:
dir_to_scan.append(bot.config.SopelGifSearch.extra)
valid_gif_api_dict = read_directory_json_to_dict(bot, dir_to_scan, "Gif API", "SpiceBot_GifSearch")
valid_gif_api_dict = SpiceBot.read_directory_json_to_dict(dir_to_scan, "Gif API", "SpiceBot_GifSearch", logging=True)
for gif_api in valid_gif_api_dict.keys():
bot.config.define_section(gif_api, GifAPISection, validate=False)
SpiceBot.config.define_section(gif_api, GifAPISection, validate=False)
apikey = eval("bot.config." + gif_api + ".apikey")
if apikey:
valid_gif_api_dict[gif_api]["apikey"] = apikey
@ -73,12 +72,17 @@ def setup(bot):
bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'][gif_api] = valid_gif_api_dict[gif_api]
for validgifapi in bot.memory["SpiceBot_GifSearch"]['valid_gif_api_dict'].keys():
commandsquery_register(bot, "prefix", validgifapi)
command_dict = {
"comtype": "prefix",
"validcoms": validgifapi
}
SpiceBot.commands.dict['counts'] += 1
SpiceBot.commands.register(command_dict)
if validgifapi not in bot.memory["SpiceBot_GifSearch"]['cache'].keys():
bot.memory["SpiceBot_GifSearch"]['cache'][validgifapi] = dict()
botevents.trigger(bot, botevents.BOT_GIFSEARCH, "SpiceBot_GifSearch")
SpiceBot.events.trigger(bot, SpiceBot.events.BOT_GIFSEARCH, "SpiceBot_GifSearch")
def shutdown(bot):

View File

@ -1,43 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs
from sopel_modules.SpiceBot.Events import botevents
def setup(bot):
if 'SpiceBot_InvalidCommand' not in bot.memory:
bot.memory["SpiceBot_InvalidCommand"] = {"valid": []}
def shutdown(bot):
if "SpiceBot_InvalidCommand" in bot.memory:
del bot.memory["SpiceBot_InvalidCommand"]
@sopel.module.event(botevents.BOT_LOADED)
@sopel.module.rule('.*')
def bot_events_complete(bot, trigger):
for comtype in bot.memory['SpiceBot_CommandsQuery']['commands'].keys():
bot.memory["SpiceBot_InvalidCommand"]["valid"].extend(bot.memory['SpiceBot_CommandsQuery']['commands'][comtype].keys())
@botevents.check_ready([botevents.BOT_LOADED, botevents.BOT_COMMANDSQUERY])
@sopel.module.commands('(.*)')
def InvalidCommand_triggers(bot, trigger):
return
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'prefix_command')
# patch for people typing "...", maybe other stuff, but this verifies that there is still a command here
if triggercommand.startswith("."):
return
if triggercommand not in bot.memory["SpiceBot_InvalidCommand"]["valid"]:
invalid_display = ["I don't seem to have a command for " + str(triggercommand) + "!"]
invalid_display.append("If you have a suggestion for this command, you can run .feature ." + str(triggercommand))
invalid_display.append("ADD DESCRIPTION HERE!")
bot.osd(invalid_display, trigger.nick, 'notice')

View File

@ -1,12 +0,0 @@
# coding=utf8
"""Sopel Gif Search
Sopel Gif Search builds a convenient framework for searching for Gifs
"""
from __future__ import unicode_literals, absolute_import, division, print_function
from .InvalidCommand import *
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.1'

View File

@ -1,96 +0,0 @@
# coding=utf8
"""Sopel Kick
Sopel Kick is an easy way to kick users from channels
"""
# pylama:ignore=W0611
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.bot
from sopel import tools, module
from sopel.tools import Identifier
from sopel.config.types import StaticSection, ValidatedAttribute
from sopel_modules.SpiceBot.Logs import botlogs
import time
from collections import abc
import sys
__author__ = 'Sam Zick'
__email__ = 'sam@deathbybandaid.net'
__version__ = '0.1.2'
def configure(config):
config.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False)
config.SpiceBot_Kick.configure_setting('kick', 'MAXTARG limit for KICK')
def setup(bot):
# Inject KICK
botlogs.log('SpiceBot_Kick', "Implanting Kick function into bot")
bot.kick = SopelKICK.kick
sopel.bot.SopelWrapper.kick = SopelWrapperKICK.kick
# verify config settings for server
botlogs.log('SpiceBot_Kick', "Checking for config settings")
bot.config.define_section("SpiceBot_Kick", SpiceBot_Kick, validate=False)
@module.event('005')
@module.rule('.*')
def parse_event_005(bot, trigger):
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]
for param in parameters:
if '=' in param:
if param.startswith("TARGMAX"):
param = str(param).split('=')[1]
settings = str(param).split(',')
for setting in settings:
settingname = str(setting).split(':')[0]
if settingname.upper() in ['KICK']:
try:
value = str(setting).split(':')[1] or None
except IndexError:
value = None
if value:
bot.config.SpiceBot_Kick.kick = int(value)
class SpiceBot_Kick(StaticSection):
kick = ValidatedAttribute('kick', default=1)
class SopelKICK:
def kick(self, nick, channel, text=None):
"""Send an IRC KICK command.
Within the context of a triggered callable, ``channel`` will default to the
channel in which the call was triggered. If triggered from a private message,
``channel`` is required (or the call to ``kick()`` will be ignored).
The bot must be a channel operator in specified channel for this to work.
.. versionadded:: 7.0
"""
self.write(['KICK', channel, nick], text)
class SopelWrapperKICK(object):
def kick(self, nick, channel=None, message=None):
if channel is None:
if self._trigger.is_privmsg:
raise RuntimeError('Error: KICK requires a channel.')
else:
channel = self._trigger.sender
if nick is None:
raise RuntimeError('Error: KICK requires a nick.')
self._bot.kick(nick, channel, message)

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals, absolute_import, division, print_function
import sopel.module
import spicemanip
from sopel_modules.SpiceBot.Logs import botlogs
from sopel_modules.SpiceBot.Events import botevents
from sopel_modules.SpiceBot_SBTools import sopel_triggerargs, command_permissions_check, inlist, inlist_match, similar_list
@botevents.check_ready([botevents.BOT_LOGS])
@sopel.module.nickname_commands('logs', 'debug')
def bot_command_logs(bot, trigger):
if not command_permissions_check(bot, trigger, ['admins', 'owner', 'OP', 'ADMIN', 'OWNER']):
bot.say("I was unable to process this Bot Nick command due to privilege issues.")
return
triggerargs, triggercommand = sopel_triggerargs(bot, trigger, 'nickname_command')
logtype = spicemanip.main(triggerargs, 1) or None
if not logtype:
bot.osd("Current valid log(s) include: " + spicemanip.main(botlogs.SpiceBot_Logs["list"].keys(), 'andlist'), trigger.sender, 'action')
return
if not inlist(bot, logtype, botlogs.SpiceBot_Logs["list"].keys()):
closestmatches = similar_list(bot, logtype, botlogs.SpiceBot_Logs["list"].keys(), 10, 'reverse')
if not len(closestmatches):
bot.notice("No valid logs match " + str(logtype) + ".", trigger.nick)
else:
bot.notice("The following commands may match " + str(logtype) + ": " + spicemanip.main(closestmatches, 'andlist') + ".", trigger.nick)
return
logtype = inlist_match(bot, logtype, botlogs.SpiceBot_Logs["list"].keys())
if logtype == "Sopel_systemd":
logindex = botlogs.systemd_logs_fetch(bot)
elif logtype == "Sopel_stdio":
logindex = botlogs.stdio_logs_fetch(bot)
else:
logindex = botlogs.SpiceBot_Logs["list"][logtype]
if not len(logindex):
bot.osd("No logs found for " + str(logtype) + ".")
return
bot.osd("Is Examining " + str(logtype) + " log(s).")
for line in logindex:
bot.osd(" " + str(line))
bot.osd(str(logtype) + " log(s) Complete.")

Some files were not shown because too many files have changed in this diff Show More