1
0
Fork 0

Standardize Bot class names

This commit is contained in:
nemunaire 2014-07-17 15:27:28 +02:00
parent f9970cba42
commit 26faed014f
5 changed files with 72 additions and 54 deletions

View File

@ -1,8 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Nemubot is a modulable IRC bot, built around XML configuration files. # Nemubot is a smart and modulable IM bot.
# Copyright (C) 2012 Mercier Pierre-Olivier # Copyright (C) 2012-2014 nemunaire
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by # it under the terms of the GNU Affero General Public License as published by
@ -22,14 +22,14 @@ import os
import imp import imp
import traceback import traceback
from nemubot import Bot import nemubot
from nemubot import prompt from nemubot import prompt
from nemubot.prompt.builtins import load_file from nemubot.prompt.builtins import load_file
from nemubot import importer from nemubot import importer
if __name__ == "__main__": if __name__ == "__main__":
# Create bot context # Create bot context
context = Bot(0, "FIXME") context = nemubot.Bot()
# Load the prompt # Load the prompt
prmpt = prompt.Prompt() prmpt = prompt.Prompt()
@ -50,7 +50,7 @@ if __name__ == "__main__":
else: else:
load_file(arg, context) load_file(arg, context)
print ("Nemubot v%s ready, my PID is %i!" % (context.version_txt, print ("Nemubot v%s ready, my PID is %i!" % (nemubot.__version__,
os.getpid())) os.getpid()))
while prmpt.run(context): while prmpt.run(context):
try: try:
@ -63,7 +63,7 @@ if __name__ == "__main__":
# Reload all other modules # Reload all other modules
bot.reload() bot.reload()
print ("\033[1;32mContext reloaded\033[0m, now in Nemubot %s" % print ("\033[1;32mContext reloaded\033[0m, now in Nemubot %s" %
context.version_txt) nemubot.__version__)
except: except:
print ("\033[1;31mUnable to reload the prompt due to errors.\033[0" print ("\033[1;31mUnable to reload the prompt due to errors.\033[0"
"m Fix them before trying to reload the prompt.") "m Fix them before trying to reload the prompt.")

View File

@ -16,12 +16,14 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime from datetime import datetime, timedelta
from datetime import timedelta from ipaddress import ip_address
import logging
from queue import Queue from queue import Queue
import threading import uuid
import time
import re __version__ = '4.0.dev0'
__author__ = 'nemunaire'
from nemubot import consumer from nemubot import consumer
from nemubot import event from nemubot import event
@ -31,28 +33,30 @@ from nemubot.IRCServer import IRCServer
from nemubot.DCC import DCC from nemubot.DCC import DCC
from nemubot import response from nemubot import response
ID_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
class Bot: class Bot:
def __init__(self, ip, realname, mp=list()):
# Bot general informations
self.version = 4.0
self.version_txt = '4.0.dev0'
# Save various informations """Class containing the bot context and ensuring key goals"""
self.ip = ip
self.realname = realname def __init__(self, ip="127.0.0.1", modules_paths=list(), data_path="./datas/"):
self.ctcp_capabilities = dict() """Initialize the bot context
self.init_ctcp_capabilities()
Keyword arguments:
ip -- The external IP of the bot (default: 127.0.0.1)
modules_paths -- Paths to all directories where looking for module
data_path -- Path to directory where store bot context data
"""
# External IP for accessing this bot
self.ip = ip_address(ip)
# Context paths
self.modules_paths = modules_paths
self.data_path = data_path
# Keep global context: servers and modules # Keep global context: servers and modules
self.servers = dict() self.servers = dict()
self.modules = dict() self.modules = dict()
# Context paths
self.modules_path = mp
self.datas_path = './datas/'
# Events # Events
self.events = list() self.events = list()
self.event_timer = None self.event_timer = None
@ -62,13 +66,16 @@ class Bot:
# Other known bots, making a bots network # Other known bots, making a bots network
self.network = dict() self.network = dict()
self.hooks_cache = dict() self._hooks_cache = dict()
# Messages to be treated # Messages to be treated
self.cnsr_queue = Queue() self.cnsr_queue = Queue()
self.cnsr_thrd = list() self.cnsr_thrd = list()
self.cnsr_thrd_size = -1 self.cnsr_thrd_size = -1
# Stop the class thread
self.stop = False
self.hooks.add_hook("irc_hook", self.hooks.add_hook("irc_hook",
hooks.Hook(self.treat_prvmsg, "PRIVMSG"), hooks.Hook(self.treat_prvmsg, "PRIVMSG"),
self) self)
@ -80,13 +87,13 @@ class Bot:
self.ctcp_capabilities["CLIENTINFO"] = self._ctcp_clientinfo self.ctcp_capabilities["CLIENTINFO"] = self._ctcp_clientinfo
self.ctcp_capabilities["DCC"] = self._ctcp_dcc self.ctcp_capabilities["DCC"] = self._ctcp_dcc
self.ctcp_capabilities["NEMUBOT"] = lambda srv, msg: _ctcp_response( self.ctcp_capabilities["NEMUBOT"] = lambda srv, msg: _ctcp_response(
msg.sender, "NEMUBOT %f" % self.version) msg.sender, "NEMUBOT %f" % __version__)
self.ctcp_capabilities["TIME"] = lambda srv, msg: _ctcp_response( self.ctcp_capabilities["TIME"] = lambda srv, msg: _ctcp_response(
msg.sender, "TIME %s" % (datetime.now())) msg.sender, "TIME %s" % (datetime.now()))
self.ctcp_capabilities["USERINFO"] = lambda srv, msg: _ctcp_response( self.ctcp_capabilities["USERINFO"] = lambda srv, msg: _ctcp_response(
msg.sender, "USERINFO %s" % self.realname) msg.sender, "USERINFO %s" % self.realname)
self.ctcp_capabilities["VERSION"] = lambda srv, msg: _ctcp_response( self.ctcp_capabilities["VERSION"] = lambda srv, msg: _ctcp_response(
msg.sender, "VERSION nemubot v%s" % self.version_txt) msg.sender, "VERSION nemubot v%s" % __version__)
def _ctcp_clientinfo(self, srv, msg): def _ctcp_clientinfo(self, srv, msg):
"""Response to CLIENTINFO CTCP message""" """Response to CLIENTINFO CTCP message"""
@ -104,6 +111,8 @@ class Bot:
print ("DCC: unable to connect to %s:%s" % (ip, msg.cmds[4])) print ("DCC: unable to connect to %s:%s" % (ip, msg.cmds[4]))
# Events methods
def add_event(self, evt, eid=None, module_src=None): def add_event(self, evt, eid=None, module_src=None):
"""Register an event and return its identifiant for futur update""" """Register an event and return its identifiant for futur update"""
if eid is None: if eid is None:
@ -125,7 +134,7 @@ class Bot:
break break
self.events.insert(i + 1, evt) self.events.insert(i + 1, evt)
if i == -1: if i == -1:
self.update_timer() self._update_event_timer()
if len(self.events) <= 0 or self.events[i+1] != evt: if len(self.events) <= 0 or self.events[i+1] != evt:
return None return None
@ -138,7 +147,7 @@ class Bot:
"""Find and remove an event from list""" """Find and remove an event from list"""
if len(self.events) > 0 and id == self.events[0].id: if len(self.events) > 0 and id == self.events[0].id:
self.events.remove(self.events[0]) self.events.remove(self.events[0])
self.update_timer() self._update_event_timer()
if module_src is not None: if module_src is not None:
module_src.REGISTERED_EVENTS.remove(evt.id) module_src.REGISTERED_EVENTS.remove(evt.id)
return True return True
@ -152,7 +161,7 @@ class Bot:
return True return True
return False return False
def update_timer(self): def _update_event_timer(self):
"""Relaunch the timer to end with the closest event""" """Relaunch the timer to end with the closest event"""
# Reset the timer if this is the first item # Reset the timer if this is the first item
if self.event_timer is not None: if self.event_timer is not None:
@ -180,9 +189,11 @@ class Bot:
self.cnsr_queue.put_nowait(consumer.EventConsumer(evt)) self.cnsr_queue.put_nowait(consumer.EventConsumer(evt))
self.update_consumers() self.update_consumers()
self.update_timer() self._update_event_timer()
# Server methods
def addServer(self, node, nick, owner, realname, ssl=False): def addServer(self, node, nick, owner, realname, ssl=False):
"""Add a new server to the context""" """Add a new server to the context"""
srv = IRCServer(node, nick, owner, realname, ssl) srv = IRCServer(node, nick, owner, realname, ssl)
@ -199,6 +210,8 @@ class Bot:
return False return False
# Modules methods
def add_module(self, module): def add_module(self, module):
"""Add a module to the context, if already exists, unload the """Add a module to the context, if already exists, unload the
old one before""" old one before"""
@ -218,8 +231,8 @@ class Bot:
if path[len(path)-1] != "/": if path[len(path)-1] != "/":
path = path + "/" path = path + "/"
if path not in self.modules_path: if path not in self.modules_paths:
self.modules_path.append(path) self.modules_paths.append(path)
return True return True
return False return False
@ -243,6 +256,9 @@ class Bot:
return True return True
return False return False
# Consumers methods
def update_consumers(self): def update_consumers(self):
"""Launch new consumer thread if necessary""" """Launch new consumer thread if necessary"""
if self.cnsr_queue.qsize() > self.cnsr_thrd_size: if self.cnsr_queue.qsize() > self.cnsr_thrd_size:
@ -292,14 +308,14 @@ class Bot:
# Hooks cache # Hooks cache
def create_cache(self, name): def create_cache(self, name):
if name not in self.hooks_cache: if name not in self._hooks_cache:
if isinstance(self.hooks.__dict__[name], list): if isinstance(self.hooks.__dict__[name], list):
self.hooks_cache[name] = list() self._hooks_cache[name] = list()
# Start by adding locals hooks # Start by adding locals hooks
for h in self.hooks.__dict__[name]: for h in self.hooks.__dict__[name]:
tpl = (h, 0, self.hooks.__dict__[name], self.hooks.bot) tpl = (h, 0, self.hooks.__dict__[name], self.hooks.bot)
self.hooks_cache[name].append(tpl) self._hooks_cache[name].append(tpl)
# Now, add extermal hooks # Now, add extermal hooks
level = 0 level = 0
@ -309,17 +325,17 @@ class Bot:
if len(self.network[ext].hooks) > level: if len(self.network[ext].hooks) > level:
lvl_exist = True lvl_exist = True
for h in self.network[ext].hooks[level].__dict__[name]: for h in self.network[ext].hooks[level].__dict__[name]:
if h not in self.hooks_cache[name]: if h not in self._hooks_cache[name]:
self.hooks_cache[name].append((h, level + 1, self._hooks_cache[name].append((h, level + 1,
self.network[ext].hooks[level].__dict__[name], self.network[ext].hooks[level].bot)) self.network[ext].hooks[level].__dict__[name], self.network[ext].hooks[level].bot))
level += 1 level += 1
elif isinstance(self.hooks.__dict__[name], dict): elif isinstance(self.hooks.__dict__[name], dict):
self.hooks_cache[name] = dict() self._hooks_cache[name] = dict()
# Start by adding locals hooks # Start by adding locals hooks
for h in self.hooks.__dict__[name]: for h in self.hooks.__dict__[name]:
self.hooks_cache[name][h] = (self.hooks.__dict__[name][h], 0, self._hooks_cache[name][h] = (self.hooks.__dict__[name][h], 0,
self.hooks.__dict__[name], self.hooks.__dict__[name],
self.hooks.bot) self.hooks.bot)
@ -331,14 +347,14 @@ class Bot:
if len(self.network[ext].hooks) > level: if len(self.network[ext].hooks) > level:
lvl_exist = True lvl_exist = True
for h in self.network[ext].hooks[level].__dict__[name]: for h in self.network[ext].hooks[level].__dict__[name]:
if h not in self.hooks_cache[name]: if h not in self._hooks_cache[name]:
self.hooks_cache[name][h] = (self.network[ext].hooks[level].__dict__[name][h], level + 1, self.network[ext].hooks[level].__dict__[name], self.network[ext].hooks[level].bot) self._hooks_cache[name][h] = (self.network[ext].hooks[level].__dict__[name][h], level + 1, self.network[ext].hooks[level].__dict__[name], self.network[ext].hooks[level].bot)
level += 1 level += 1
else: else:
raise Exception(name + " hook type unrecognized") raise Exception(name + " hook type unrecognized")
return self.hooks_cache[name] return self._hooks_cache[name]
# Treatment # Treatment
@ -603,7 +619,7 @@ def _help_msg(sndr, modules, cmd):
return res return res
def hotswap(bak): def hotswap(bak):
return Bot(bak.servers, bak.modules, bak.modules_path) return Bot(bak.servers, bak.modules, bak.modules_paths)
def reload(): def reload():
import imp import imp

View File

@ -56,8 +56,8 @@ class MessagesHook:
"add_hook function, please fix it in order to be " "add_hook function, please fix it in order to be "
"compatible with unload feature") "compatible with unload feature")
if store in self.context.hooks_cache: if store in self.context._hooks_cache:
del self.context.hooks_cache[store] del self.context._hooks_cache[store]
if not hasattr(self, store): if not hasattr(self, store):
print ("\033[1;35mWarning:\033[0m unrecognized hook store") print ("\033[1;35mWarning:\033[0m unrecognized hook store")
@ -143,8 +143,8 @@ class MessagesHook:
def del_hook(self, store, hook, module_src=None): def del_hook(self, store, hook, module_src=None):
"""Remove a registered hook from a given store""" """Remove a registered hook from a given store"""
if store in self.context.hooks_cache: if store in self.context._hooks_cache:
del self.context.hooks_cache[store] del self.context._hooks_cache[store]
if not hasattr(self, store): if not hasattr(self, store):
print ("Warning: unrecognized hook store type") print ("Warning: unrecognized hook store type")

View File

@ -16,12 +16,14 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from distutils.version import StrictVersion
from importlib.abc import Finder from importlib.abc import Finder
from importlib.abc import SourceLoader from importlib.abc import SourceLoader
import imp import imp
import os import os
import sys import sys
import nemubot
from nemubot import event from nemubot import event
from nemubot import exception from nemubot import exception
from nemubot.hooks import Hook from nemubot.hooks import Hook
@ -37,7 +39,7 @@ class ModuleFinder(Finder):
#print ("looking for", fullname, "in", path) #print ("looking for", fullname, "in", path)
# Search only for new nemubot modules (packages init) # Search only for new nemubot modules (packages init)
if path is None: if path is None:
for mpath in self.context.modules_path: for mpath in self.context.modules_paths:
#print ("looking for", fullname, "in", mpath) #print ("looking for", fullname, "in", mpath)
if os.path.isfile(mpath + fullname + ".xml"): if os.path.isfile(mpath + fullname + ".xml"):
return ModuleLoader(self.context, self.prompt, fullname, return ModuleLoader(self.context, self.prompt, fullname,
@ -137,7 +139,7 @@ class ModuleLoader(SourceLoader):
if not hasattr(module, "nemubotversion"): if not hasattr(module, "nemubotversion"):
raise ImportError("Module `%s' is not a nemubot module."%self.name) raise ImportError("Module `%s' is not a nemubot module."%self.name)
# Check module version # Check module version
if module.nemubotversion != self.context.version: if StrictVersion(module.nemubotversion) != StrictVersion(nemubot.__version__):
raise ImportError("Module `%s' is not compatible with this " raise ImportError("Module `%s' is not compatible with this "
"version." % self.name) "version." % self.name)

View File

@ -12,7 +12,7 @@ except ImportError:
with open(os.path.join(os.path.dirname(__file__), with open(os.path.join(os.path.dirname(__file__),
'lib', 'lib',
'__init__.py')) as f: '__init__.py')) as f:
version = re.search("self.version_txt = '([^']+)'", f.read()).group(1) version = re.search("__version__ = '([^']+)'", f.read()).group(1)
with open('requirements.txt', 'r') as f: with open('requirements.txt', 'r') as f:
requires = [x.strip() for x in f if x.strip()] requires = [x.strip() for x in f if x.strip()]