Merge pull request #94 from deathbybandaid/development

Development
This commit is contained in:
Deathbybandaid 2019-07-05 08:43:09 -04:00 committed by GitHub
commit 3dda343fbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 350 additions and 22 deletions

View File

@ -20,6 +20,9 @@ import threading
BASE = declarative_base()
MYSQL_TABLE_ARGS = {'mysql_engine': 'InnoDB',
'mysql_charset': 'utf8mb4',
'mysql_collate': 'utf8mb4_unicode_ci'}
class NickIDs(BASE):
@ -35,7 +38,7 @@ class Nicknames(BASE):
Nicknames SQLAlchemy Class
"""
__tablename__ = 'spice_nicknames'
__table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'}
__table_args__ = MYSQL_TABLE_ARGS
nick_id = Column(Integer, ForeignKey('spice_nick_ids.nick_id'), primary_key=True)
slug = Column(String(255), primary_key=True)
canonical = Column(String(255))
@ -46,7 +49,7 @@ class NickValues(BASE):
NickValues SQLAlchemy Class
"""
__tablename__ = 'spice_nick_values'
__table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'}
__table_args__ = MYSQL_TABLE_ARGS
nick_id = Column(Integer, ForeignKey('spice_nick_ids.nick_id'), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
@ -58,19 +61,31 @@ class ChannelValues(BASE):
ChannelValues SQLAlchemy Class
"""
__tablename__ = 'spice_channel_values'
__table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'}
__table_args__ = MYSQL_TABLE_ARGS
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 ServerValues(BASE):
"""
ServerValues SQLAlchemy Class
"""
__tablename__ = 'spice_server_values'
__table_args__ = MYSQL_TABLE_ARGS
server = 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'
__table_args__ = {'mysql_engine': 'InnoDB', 'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'}
__table_args__ = MYSQL_TABLE_ARGS
plugin = Column(String(255), primary_key=True)
namespace = Column(String(255), primary_key=True)
key = Column(String(255), primary_key=True)
@ -393,11 +408,148 @@ class SpiceDB(object):
finally:
session.close()
# SERVER FUNCTIONS
def set_server_value(self, server, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the server."""
server = server.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ServerValues) \
.filter(ServerValues.server == server)\
.filter(ServerValues.namespace == namespace)\
.filter(ServerValues.key == key) \
.one_or_none()
# ServerValue exists, update
if result:
result.value = value
session.commit()
# DNE - Insert
else:
new_servervalue = ServerValues(server=server, namespace=namespace, key=key, value=value)
session.add(new_servervalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def get_server_value(self, server, key, namespace='default'):
"""Retrieves the value for a given key associated with a server."""
server = server.lower()
session = self.ssession()
try:
result = session.query(ServerValues) \
.filter(ServerValues.server == server)\
.filter(ServerValues.namespace == namespace)\
.filter(ServerValues.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_server_value(self, server, key, namespace='default'):
"""Sets the value for a given key to be associated with the server."""
server = server.lower()
session = self.ssession()
try:
result = session.query(ServerValues) \
.filter(ServerValues.server == server)\
.filter(ServerValues.namespace == namespace)\
.filter(ServerValues.key == key) \
.one_or_none()
# ServerValue exists, delete
if result:
session.delete(result)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_server_value(self, server, key, value, namespace='default'):
"""Sets the value for a given key to be associated with the server."""
server = server.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ServerValues) \
.filter(ServerValues.server == server)\
.filter(ServerValues.namespace == namespace)\
.filter(ServerValues.key == key) \
.one_or_none()
# ServerValue exists, update
if result:
result.value = float(result.value) + float(value)
session.commit()
# DNE - Insert
else:
new_servervalue = ServerValues(server=server, namespace=namespace, key=key, value=float(value))
session.add(new_servervalue)
session.commit()
except SQLAlchemyError:
session.rollback()
raise
finally:
session.close()
def adjust_server_list(self, server, key, entries, adjustmentdirection, namespace='default'):
"""Sets the value for a given key to be associated with the server."""
server = server.lower()
if not isinstance(entries, list):
entries = [entries]
entries = json.dumps(entries, ensure_ascii=False)
session = self.ssession()
try:
result = session.query(ServerValues) \
.filter(ServerValues.server == server)\
.filter(ServerValues.namespace == namespace)\
.filter(ServerValues.key == key) \
.one_or_none()
# ServerValue 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_servervalue = ServerValues(server=server, namespace=namespace, key=key, value=values)
session.add(new_servervalue)
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()
plugin = plugin.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
@ -423,7 +575,7 @@ class SpiceDB(object):
def get_plugin_value(self, plugin, key, namespace='default'):
"""Retrieves the value for a given key associated with a plugin."""
plugin = Identifier(plugin).lower()
plugin = plugin.lower()
session = self.ssession()
try:
result = session.query(PluginValues) \
@ -442,7 +594,7 @@ class SpiceDB(object):
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()
plugin = plugin.lower()
session = self.ssession()
try:
result = session.query(PluginValues) \
@ -462,7 +614,7 @@ class SpiceDB(object):
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()
plugin = plugin.lower()
value = json.dumps(value, ensure_ascii=False)
session = self.ssession()
try:
@ -471,7 +623,7 @@ class SpiceDB(object):
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# ChannelValue exists, update
# PluginValue exists, update
if result:
result.value = float(result.value) + float(value)
session.commit()
@ -488,7 +640,7 @@ class SpiceDB(object):
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()
plugin = plugin.lower()
if not isinstance(entries, list):
entries = [entries]
entries = json.dumps(entries, ensure_ascii=False)
@ -499,7 +651,7 @@ class SpiceDB(object):
.filter(PluginValues.namespace == namespace)\
.filter(PluginValues.key == key) \
.one_or_none()
# ChannelValue exists, update
# PluginValue exists, update
if result:
if adjustmentdirection == 'add':
for entry in entries:
@ -554,6 +706,13 @@ class BotDatabase():
SopelDB.adjust_channel_value = SpiceDB.adjust_channel_value
SopelDB.adjust_channel_list = SpiceDB.adjust_channel_list
sopel.db.ServerValues = ServerValues
SopelDB.get_server_value = SpiceDB.get_server_value
SopelDB.set_server_value = SpiceDB.set_server_value
SopelDB.delete_server_value = SpiceDB.delete_server_value
SopelDB.adjust_server_value = SpiceDB.adjust_server_value
SopelDB.adjust_server_list = SpiceDB.adjust_server_list
sopel.db.PluginValues = PluginValues
SopelDB.get_plugin_value = SpiceDB.get_plugin_value
SopelDB.set_plugin_value = SpiceDB.set_plugin_value
@ -631,6 +790,23 @@ class BotDatabase():
def adjust_channel_list(self, nick, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust_channel_list(nick, key, entries, adjustmentdirection, namespace)
"""Servers"""
def get_server_value(self, _server, key, namespace='default'):
return self.db.get__server_value(_server, key, namespace)
def set__server_value(self, _server, key, value, namespace='default'):
return self.db.set__server_value(_server, key, value, namespace)
def delete__server_value(self, _server, key, namespace='default'):
return self.db.delete__server_value(_server, key, namespace)
def adjust__server_value(self, _server, key, value, namespace='default'):
return self.db.adjust__server_value(_server, key, value, namespace)
def adjust__server_list(self, nick, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust__server_list(nick, key, entries, adjustmentdirection, namespace)
"""Plugins"""
def get_plugin_value(self, plugin, key, namespace='default'):
@ -645,8 +821,8 @@ class BotDatabase():
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)
def adjust_plugin_list(self, plugin, key, entries, adjustmentdirection, namespace='default'):
return self.db.adjust_plugin_list(plugin, key, entries, adjustmentdirection, namespace)
db = BotDatabase()

View File

@ -35,8 +35,10 @@ class BotEvents(object):
def __init__(self):
self.lock = threading.Lock()
self.RPL_WELCOME = '001' # This is a defined IRC event
self.RPL_NAMREPLY = '353' # This is a defined IRC event
self.RPL_MYINFO = '004' # This is a defined IRC event
self.RPL_ISUPPORT = '005' # This is a defined IRC event
self.RPL_WHOREPLY = '352' # This is a defined IRC event
self.RPL_NAMREPLY = '353' # This is a defined IRC event
self.BOT_UPTIME = time.time()
self.BOT_WELCOME = '1001'
self.BOT_READY = '1002'

View File

@ -119,6 +119,14 @@ def prerun_query(t_command_type='module', t_command_subtype=None):
@functools.wraps(function)
def internal_prerun(bot, trigger, *args, **kwargs):
# verify the bot is at a loaded state
while not botevents.check(botevents.BOT_LOADED):
pass
# Verify channel and user exist
verify_channel(trigger)
verify_user(trigger)
botcom = class_create('botcom')
if t_command_type == "nickname":
@ -410,6 +418,7 @@ def trigger_hyphen_args(trigger_args_part):
def trigger_hyphen_arg_handler(bot, trigger):
# Commands that cannot run via privmsg
# TODO --check should work for commands that don't exist
if trigger.sb["hyphen_arg"] in ['check']:
if trigger.sb["com"].lower() != trigger.sb["realcom"]:
botmessagelog.messagelog(trigger.sb["log_id"], trigger.sb["comtext"] + " is a valid alias command for " + trigger.sb["realcomtext"])

View File

@ -0,0 +1,87 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Server system.
"""
from .Config import config as botconfig
class BotServer():
"""This Logs all server values of relevance'"""
def __init__(self):
self.linenumber = 0
self.dict = {
"host_connect": botconfig.core.host,
"host": botconfig.core.host,
}
self.isupport = {
"NETWORK": botconfig.core.host,
"TARGMAX": {
"KICK": 1,
'NOTICE': 1,
'PRIVMSG': 1,
},
}
self.myinfo = {
"servername": botconfig.core.host,
"version": None,
"usermodes": [],
"channelmodes": []
}
def rpl_welcome(self, trigger):
self.dict["host"] = str(trigger.sender).lower()
def parse_reply_myinfo(self, trigger):
self.myinfo["servername"] = trigger.args[1]
self.myinfo["version"] = trigger.args[2]
self.myinfo["usermodes"] = list(trigger.args[3])
self.myinfo["channelmodes"] = list(trigger.args[4])
def parse_reply_isupport(self, trigger):
# check against 005_Bounce
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]
for param in parameters:
# check for value associated with the parameter
if '=' not in param:
self.isupport[str(param)] = None
else:
key, raw_value = param.split('=')
if ',' not in raw_value:
if str(raw_value).isdigit():
setting_value = int(raw_value)
self.isupport[str(key)] = raw_value
else:
if str(key) not in list(self.isupport.keys()):
self.isupport[str(key)] = {}
if not isinstance(self.isupport[str(key)], dict):
self.isupport[str(key)] = {}
settings = str(raw_value).split(',')
for setting in settings:
if ":" not in setting:
self.isupport[str(key)][str(setting)] = None
else:
setting_name, setting_value = setting.split(":")
try:
setting_value = str(setting).split(':')[1] or None
except IndexError:
setting_value = None
if str(setting_value).isdigit():
setting_value = int(setting_value)
self.isupport[str(key)][str(setting_name)] = setting_value
server = BotServer()

View File

@ -65,6 +65,7 @@ class BotUsers():
except Exception as e:
nick_id = e
nick_id = None
return None
if usercreate and nick_id:
self.add_to_all(nick, nick_id)
return int(nick_id)

View File

@ -21,6 +21,7 @@ from .Prerun import *
from .Commands import *
from .Events import *
from .StartupMonologue import *
from .Server import *
from .Channels import *
from .Users import *
from .AI import *

View File

@ -12,6 +12,8 @@ import time
from collections import abc
import sys
from .Server import server as botserver
class SpiceBot_OSD(StaticSection):
@ -62,9 +64,9 @@ class ToolsOSD:
raise ValueError("Recipients list empty.")
if text_method == 'NOTICE':
maxtargets = bot.config.SpiceBot_OSD.notice
maxtargets = botserver.isupport["TARGMAX"]["NOTICE"]
elif text_method in ['PRIVMSG', 'ACTION']:
maxtargets = bot.config.SpiceBot_OSD.privmsg
maxtargets = botserver.isupport["TARGMAX"]["PRIVMSG"]
maxtargets = int(maxtargets)
recipientgroups = []

View File

@ -12,5 +12,5 @@ import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.prerun('nickname')
@sopel.module.nickname_commands('database')
def bot_command_database(bot, trigger):
def bot_command_database(bot, trigger, botcom):
bot.osd("Database is " + SpiceBot.config.db_type)

View File

@ -9,7 +9,7 @@ import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.prerun('nickname')
@sopel.module.nickname_commands('help', 'docs', 'wiki')
def bot_command_docs(bot, trigger):
def bot_command_docs(bot, trigger, botcom):
sb_git_url = str(
SpiceBot.github_dict["url_main"] +
SpiceBot.github_dict["repo_owner"] + "/" +

View File

@ -0,0 +1,17 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Users system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@SpiceBot.prerun('nickname')
@sopel.module.nickname_commands('server')
def nickname_comand_server(bot, trigger, botcom):
network = SpiceBot.server.isupport["NETWORK"]
server = SpiceBot.server.myinfo["servername"]
version = SpiceBot.server.myinfo["version"]
bot.osd("I am connected to " + str(server) + " on the " + str(network) + " network. This server runs " + str(version))

View File

@ -9,6 +9,7 @@ This module contains references only for other modules to utilize.
from .CommandsQuery import *
from .Commands import *
from .Server import *
from .Channels import *
from .Users import *
from .Database import *

View File

@ -5,10 +5,12 @@ This is the SpiceBot OSD system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event('005')
@sopel.module.event(SpiceBot.events.RPL_ISUPPORT)
@sopel.module.rule('.*')
def parse_event_005(bot, trigger):
def parse_event_005_kick(bot, trigger):
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]

View File

@ -0,0 +1,26 @@
# coding=utf8
from __future__ import unicode_literals, absolute_import, division, print_function
"""
This is the SpiceBot Channels system.
"""
import sopel
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.RPL_WELCOME)
@sopel.module.rule('.*')
def server_name(bot, trigger):
SpiceBot.server.rpl_welcome(trigger)
@sopel.module.event(SpiceBot.events.RPL_ISUPPORT)
@sopel.module.rule('.*')
def parse_event_005(bot, trigger):
SpiceBot.server.parse_reply_isupport(trigger)
@sopel.module.event(SpiceBot.events.RPL_MYINFO)
@sopel.module.rule('.*')
def parse_event_004(bot, trigger):
SpiceBot.server.parse_reply_myinfo(trigger)

View File

@ -7,6 +7,7 @@ This module contains references only for other modules to utilize.
"""
# pylama:ignore=W0401,W0611
from .Server import *
from .Channels import *
from .Users import *
from .Events import *

View File

@ -6,9 +6,12 @@ This is the SpiceBot OSD system.
import sopel
@sopel.module.event('005')
import sopel_modules.SpiceBot as SpiceBot
@sopel.module.event(SpiceBot.events.RPL_ISUPPORT)
@sopel.module.rule('.*')
def parse_event_005(bot, trigger):
def parse_event_005_osd(bot, trigger):
if trigger.args[-1] != 'are supported by this server':
return
parameters = trigger.args[1:-1]