Make some stuff nicer
This commit is contained in:
parent
0d3fbfc91c
commit
19b7a95d76
21
bin/pyircbot
21
bin/pyircbot
|
@ -1,29 +1,30 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
from optparse import OptionParser
|
from argparse import ArgumentParser
|
||||||
from pyircbot import PyIRCBot
|
from pyircbot import PyIRCBot
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
" logging level and facility "
|
" logging level and facility "
|
||||||
logging.basicConfig(level=logging.DEBUG, format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
|
||||||
log = logging.getLogger('main')
|
log = logging.getLogger('main')
|
||||||
|
|
||||||
" parse command line args "
|
" parse command line args "
|
||||||
parser = OptionParser()
|
parser = ArgumentParser(description="Run pyircbot")
|
||||||
parser.add_option("-c", "--config", action="store", type="string", dest="config", help="Path to config file")
|
parser.add_argument("-c", "--config", help="Path to config file", required=True)
|
||||||
|
parser.add_argument("--debug", action="store_true", help="Dump raw irc network")
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
log.debug(options)
|
if args.debug:
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
log.debug(args)
|
||||||
|
|
||||||
if not options.config:
|
if not args.config:
|
||||||
log.critical("No bot config file specified (-c). Exiting.")
|
log.critical("No bot config file specified (-c). Exiting.")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
botconfig = PyIRCBot.load(options.config)
|
botconfig = PyIRCBot.load(args.config)
|
||||||
|
|
||||||
log.debug(botconfig)
|
log.debug(botconfig)
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,19 @@ import sys
|
||||||
from inspect import getargspec
|
from inspect import getargspec
|
||||||
from socket import SHUT_RDWR
|
from socket import SHUT_RDWR
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep,time
|
from time import sleep, time
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
except:
|
except:
|
||||||
from io import BytesIO as StringIO
|
from io import BytesIO as StringIO
|
||||||
|
|
||||||
|
IRCEvent = namedtuple("IRCEvent", "args prefix trailing")
|
||||||
|
UserPrefix = namedtuple("UserPrefix", "nick username hostname")
|
||||||
|
ServerPrefix = namedtuple("ServerPrefix", "hostname")
|
||||||
|
|
||||||
|
|
||||||
class IRCCore(asynchat.async_chat):
|
class IRCCore(asynchat.async_chat):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -120,7 +126,7 @@ class IRCCore(asynchat.async_chat):
|
||||||
|
|
||||||
:param data: the data that was recieved
|
:param data: the data that was recieved
|
||||||
:type data: str"""
|
:type data: str"""
|
||||||
#self.log.debug("<< %(message)s", {"message":repr(data)})
|
#self.log.info("<< %(message)s", {"message":repr(data)})
|
||||||
self.buffer.write(data)
|
self.buffer.write(data)
|
||||||
|
|
||||||
def found_terminator(self):
|
def found_terminator(self):
|
||||||
|
@ -135,11 +141,12 @@ class IRCCore(asynchat.async_chat):
|
||||||
self.log.error("found_terminator(): repr(data): %s" % repr(line))
|
self.log.error("found_terminator(): repr(data): %s" % repr(line))
|
||||||
self.log.error("found_terminator(): error: %s" % str(ude))
|
self.log.error("found_terminator(): error: %s" % str(ude))
|
||||||
return
|
return
|
||||||
|
self.log.debug("< {}".format(line))
|
||||||
self.process_data(line)
|
self.process_data(line)
|
||||||
|
|
||||||
def handle_close(self):
|
def handle_close(self):
|
||||||
"""Called when the socket is disconnected. Triggers the _DISCONNECT hook"""
|
"""Called when the socket is disconnected. Triggers the _DISCONNECT hook"""
|
||||||
self.log.debug("handle_close")
|
self.log.info("handle_close")
|
||||||
self.connected=False
|
self.connected=False
|
||||||
self.close()
|
self.close()
|
||||||
self.fire_hook("_DISCONNECT")
|
self.fire_hook("_DISCONNECT")
|
||||||
|
@ -157,16 +164,16 @@ class IRCCore(asynchat.async_chat):
|
||||||
if self.server >= len(self.servers):
|
if self.server >= len(self.servers):
|
||||||
self.server=0
|
self.server=0
|
||||||
serverHostname = self.servers[self.server]
|
serverHostname = self.servers[self.server]
|
||||||
self.log.debug("Connecting to %(server)s:%(port)i", {"server":serverHostname, "port":self.port})
|
self.log.info("Connecting to %(server)s:%(port)i", {"server":serverHostname, "port":self.port})
|
||||||
socket_type = socket.AF_INET
|
socket_type = socket.AF_INET
|
||||||
if self.ipv6:
|
if self.ipv6:
|
||||||
self.log.info("IPv6 is enabled.")
|
self.log.info("IPv6 is enabled.")
|
||||||
socket_type = socket.AF_INET6
|
socket_type = socket.AF_INET6
|
||||||
socketInfo = socket.getaddrinfo(serverHostname, self.port, socket_type)
|
socketInfo = socket.getaddrinfo(serverHostname, self.port, socket_type)
|
||||||
self.create_socket(socket_type, socket.SOCK_STREAM)
|
self.create_socket(socket_type, socket.SOCK_STREAM)
|
||||||
self.log.debug("Socket created: %s" % self.socket.fileno())
|
self.log.info("Socket created: %s" % self.socket.fileno())
|
||||||
self.connect(socketInfo[0][4])
|
self.connect(socketInfo[0][4])
|
||||||
self.log.debug("Connection established")
|
self.log.info("Connection established")
|
||||||
self._fileno = self.socket.fileno()
|
self._fileno = self.socket.fileno()
|
||||||
self.asynmap[self._fileno] = self # http://willpython.blogspot.com/2010/08/multiple-event-loops-with-asyncore-and.html
|
self.asynmap[self._fileno] = self # http://willpython.blogspot.com/2010/08/multiple-event-loops-with-asyncore-and.html
|
||||||
self.log.info("_connect: Socket map: %s" % str(self.asynmap))
|
self.log.info("_connect: Socket map: %s" % str(self.asynmap))
|
||||||
|
@ -174,9 +181,9 @@ class IRCCore(asynchat.async_chat):
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
"""When asynchat indicates our socket is connected, fire the _CONNECT hook"""
|
"""When asynchat indicates our socket is connected, fire the _CONNECT hook"""
|
||||||
self.connected=True
|
self.connected=True
|
||||||
self.log.debug("handle_connect: connected")
|
self.log.info("handle_connect: connected")
|
||||||
self.fire_hook("_CONNECT")
|
self.fire_hook("_CONNECT")
|
||||||
self.log.debug("handle_connect: complete")
|
self.log.info("handle_connect: complete")
|
||||||
|
|
||||||
def sendRaw(self, text, prio=2):
|
def sendRaw(self, text, prio=2):
|
||||||
"""Queue messages (raw string) to be sent to the IRC server
|
"""Queue messages (raw string) to be sent to the IRC server
|
||||||
|
@ -325,11 +332,9 @@ class IRCCore(asynchat.async_chat):
|
||||||
:type trailing: str
|
:type trailing: str
|
||||||
:returns: object -- a IRCEvent object with the ``args``, ``prefix``, ``trailing``"""
|
:returns: object -- a IRCEvent object with the ``args``, ``prefix``, ``trailing``"""
|
||||||
|
|
||||||
return type('IRCEvent', (object,), {
|
return IRCEvent(args,
|
||||||
"args": args,
|
IRCCore.decodePrefix(prefix) if prefix else None,
|
||||||
"prefix": IRCCore.decodePrefix(prefix) if prefix else None,
|
trailing)
|
||||||
"trailing": trailing
|
|
||||||
})
|
|
||||||
|
|
||||||
" Utility methods "
|
" Utility methods "
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -340,16 +345,11 @@ class IRCCore(asynchat.async_chat):
|
||||||
:type prefix: str
|
:type prefix: str
|
||||||
:returns: object -- an UserPrefix object with the properties `nick`, `username`, `hostname` or a ServerPrefix object with the property `hostname`"""
|
:returns: object -- an UserPrefix object with the properties `nick`, `username`, `hostname` or a ServerPrefix object with the property `hostname`"""
|
||||||
if "!" in prefix:
|
if "!" in prefix:
|
||||||
ob = type('UserPrefix', (object,), {})
|
nick, prefix = prefix.split("!")
|
||||||
ob.str = prefix
|
username, hostname = prefix.split("@")
|
||||||
ob.nick, prefix = prefix.split("!")
|
return UserPrefix(nick, username, hostname)
|
||||||
ob.username, ob.hostname = prefix.split("@")
|
|
||||||
return ob
|
|
||||||
else:
|
else:
|
||||||
ob = type('ServerPrefix', (object,), {})
|
return ServerPrefix(prefix)
|
||||||
ob.str = prefix
|
|
||||||
ob.hostname = prefix
|
|
||||||
return ob
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def trace():
|
def trace():
|
||||||
|
@ -488,15 +488,16 @@ class OutputQueueRunner(Thread):
|
||||||
self.process_queue_item()
|
self.process_queue_item()
|
||||||
lastSend = time()
|
lastSend = time()
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
#self.log.debug("Queue is empty")
|
#self.log.info("Queue is empty")
|
||||||
pass
|
pass
|
||||||
sleep(0.01)
|
sleep(0.01)
|
||||||
|
|
||||||
def process_queue_item(self):
|
def process_queue_item(self):
|
||||||
"""Remove 1 item from queue and process it"""
|
"""Remove 1 item from queue and process it"""
|
||||||
prio,text = self.bot.outputQueue.get(block=True, timeout=10)
|
prio,text = self.bot.outputQueue.get(block=True, timeout=10)
|
||||||
#self.log.debug("%s>> %s" % (prio,text))
|
#self.log.info("%s>> %s" % (prio,text))
|
||||||
self.bot.outputQueue.task_done()
|
self.bot.outputQueue.task_done()
|
||||||
|
self.log.debug("> {}".format(text.decode('UTF-8')))
|
||||||
self.bot.send(text)
|
self.bot.send(text)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
@ -507,7 +508,7 @@ class OutputQueueRunner(Thread):
|
||||||
self.bot.outputQueue.get(block=False)
|
self.bot.outputQueue.get(block=False)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
pass
|
pass
|
||||||
#self.log.debug("output queue cleared")
|
#self.log.info("output queue cleared")
|
||||||
return length
|
return length
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
|
@ -517,4 +518,4 @@ class OutputQueueRunner(Thread):
|
||||||
self.process_queue_item()
|
self.process_queue_item()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
#self.log.debug("output queue flushed")
|
#self.log.info("output queue flushed")
|
||||||
|
|
|
@ -166,9 +166,9 @@ class AttributeStorage(ModuleBase):
|
||||||
if value == None:
|
if value == None:
|
||||||
# delete it
|
# delete it
|
||||||
c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", (itemId, attributeId))
|
c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", (itemId, attributeId))
|
||||||
self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value))
|
self.log.info("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value))
|
||||||
else:
|
else:
|
||||||
# add attribute
|
# add attribute
|
||||||
c = self.db.connection.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (%s, %s, %s);", (itemId, attributeId, value))
|
c = self.db.connection.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (%s, %s, %s);", (itemId, attributeId, value))
|
||||||
self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value))
|
self.log.info("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value))
|
||||||
c.close()
|
c.close()
|
||||||
|
|
|
@ -161,9 +161,9 @@ class AttributeStorageLite(ModuleBase):
|
||||||
if value == None:
|
if value == None:
|
||||||
# delete it
|
# delete it
|
||||||
c = self.db.query("DELETE FROM `values` WHERE `itemid`=? AND `attributeid`=? ;", (itemId, attributeId))
|
c = self.db.query("DELETE FROM `values` WHERE `itemid`=? AND `attributeid`=? ;", (itemId, attributeId))
|
||||||
self.log.debug("Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value))
|
self.log.info("Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value))
|
||||||
else:
|
else:
|
||||||
# add attribute
|
# add attribute
|
||||||
c = self.db.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (?, ?, ?);", (itemId, attributeId, value))
|
c = self.db.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (?, ?, ?);", (itemId, attributeId, value))
|
||||||
self.log.debug("Stored item %s attribute %s value: %s" % (itemId, attributeId, value))
|
self.log.info("Stored item %s attribute %s value: %s" % (itemId, attributeId, value))
|
||||||
c.close()
|
c.close()
|
||||||
|
|
|
@ -121,13 +121,13 @@ class BitcoinRPC:
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
# internal. connect to the service
|
# internal. connect to the service
|
||||||
self.log.debug("CryptoWalletRPC: %s: Connecting to %s:%s" % (self.name, self.host,self.port))
|
self.log.info("CryptoWalletRPC: %s: Connecting to %s:%s" % (self.name, self.host,self.port))
|
||||||
try:
|
try:
|
||||||
self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.username, self.password, self.host, self.port))
|
self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.username, self.password, self.host, self.port))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.debug("CryptoWalletRPC: %s: Could not connect to %s:%s: %s" % (self.name, self.host, self.port, str(e)))
|
self.log.info("CryptoWalletRPC: %s: Could not connect to %s:%s: %s" % (self.name, self.host, self.port, str(e)))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log.debug("CryptoWalletRPC: %s: Connected to %s:%s" % (self.name, self.host, self.port))
|
self.log.info("CryptoWalletRPC: %s: Connected to %s:%s" % (self.name, self.host, self.port))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ class DogeController:
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
"Connect to RPC endpoint"
|
"Connect to RPC endpoint"
|
||||||
self.log.debug("DogeRPC: Connecting to dogecoind")
|
self.log.info("DogeRPC: Connecting to dogecoind")
|
||||||
self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.config["username"], self.config["password"], self.config["host"], self.config["port"]))
|
self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.config["username"], self.config["password"], self.config["host"], self.config["port"]))
|
||||||
self.con.getinfo()
|
self.con.getinfo()
|
||||||
self.log.debug("DogeRPC: Connected to %s:%s" % (self.config["host"], self.config["port"]))
|
self.log.info("DogeRPC: Connected to %s:%s" % (self.config["host"], self.config["port"]))
|
||||||
|
|
|
@ -145,7 +145,7 @@ class scrambleGame:
|
||||||
self.nextTimer.start()
|
self.nextTimer.start()
|
||||||
self.guesses=0
|
self.guesses=0
|
||||||
self.category_count+=1
|
self.category_count+=1
|
||||||
self.master.log.debug("DogeScramble: category_count is: %s" % (self.category_count))
|
self.master.log.info("DogeScramble: category_count is: %s" % (self.category_count))
|
||||||
if self.category_count >= self.change_category_after_words:
|
if self.category_count >= self.change_category_after_words:
|
||||||
self.should_change_category = True
|
self.should_change_category = True
|
||||||
else:
|
else:
|
||||||
|
@ -247,7 +247,7 @@ class scrambleGame:
|
||||||
picked = f.readline().strip().lower()
|
picked = f.readline().strip().lower()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
self.master.log.debug("DogeScramble: picked %s for %s" % (picked, self.channel))
|
self.master.log.info("DogeScramble: picked %s for %s" % (picked, self.channel))
|
||||||
self.lastwords.append(picked)
|
self.lastwords.append(picked)
|
||||||
if len(self.lastwords) > 5:
|
if len(self.lastwords) > 5:
|
||||||
self.lastwords.pop(0)
|
self.lastwords.pop(0)
|
||||||
|
|
|
@ -111,13 +111,13 @@ class LinkTitler(ModuleBase):
|
||||||
|
|
||||||
def url_headers(self, url):
|
def url_headers(self, url):
|
||||||
"HEAD requests a url to check content type & length, returns something like: {'type': 'image/jpeg', 'size': '90583'}"
|
"HEAD requests a url to check content type & length, returns something like: {'type': 'image/jpeg', 'size': '90583'}"
|
||||||
self.log.debug("url_headers(%s)" % (url,))
|
self.log.info("url_headers(%s)" % (url,))
|
||||||
resp = head(url=url, allow_redirects=True)
|
resp = head(url=url, allow_redirects=True)
|
||||||
return resp.headers
|
return resp.headers
|
||||||
|
|
||||||
def url_htmltitle(self, url):
|
def url_htmltitle(self, url):
|
||||||
"Requests page html and returns title in a safe way"
|
"Requests page html and returns title in a safe way"
|
||||||
self.log.debug("url_htmltitle(%s)" % (url,))
|
self.log.info("url_htmltitle(%s)" % (url,))
|
||||||
resp = get(url=url, stream=True)
|
resp = get(url=url, stream=True)
|
||||||
# Fetch no more than first 10kb
|
# Fetch no more than first 10kb
|
||||||
# if the title isn't seen by then, you're doing it wrong
|
# if the title isn't seen by then, you're doing it wrong
|
||||||
|
|
|
@ -41,6 +41,6 @@ class Triggered(ModuleBase):
|
||||||
|
|
||||||
def scream(self, channel):
|
def scream(self, channel):
|
||||||
delay = randrange(self.config["mindelay"], self.config["maxdelay"])
|
delay = randrange(self.config["mindelay"], self.config["maxdelay"])
|
||||||
self.log.debug("Sleeping for %s seconds" % delay)
|
self.log.info("Sleeping for %s seconds" % delay)
|
||||||
sleep(delay)
|
sleep(delay)
|
||||||
self.bot.act_PRIVMSG(channel, choice(self.config["responses"]))
|
self.bot.act_PRIVMSG(channel, choice(self.config["responses"]))
|
||||||
|
|
|
@ -12,8 +12,12 @@ import sys
|
||||||
import traceback
|
import traceback
|
||||||
from pyircbot.rpc import BotRPC
|
from pyircbot.rpc import BotRPC
|
||||||
from pyircbot.irccore import IRCCore
|
from pyircbot.irccore import IRCCore
|
||||||
|
from collections import namedtuple
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
ParsedCommand = namedtuple("ParsedCommand", "command args args_str message")
|
||||||
|
|
||||||
|
|
||||||
class PyIRCBot:
|
class PyIRCBot:
|
||||||
""":param botconfig: The configuration of this instance of the bot. Passed by main.py.
|
""":param botconfig: The configuration of this instance of the bot. Passed by main.py.
|
||||||
:type botconfig: dict
|
:type botconfig: dict
|
||||||
|
@ -126,7 +130,7 @@ class PyIRCBot:
|
||||||
:type moduleName: str"""
|
:type moduleName: str"""
|
||||||
" check if already exists "
|
" check if already exists "
|
||||||
if not name in self.modules:
|
if not name in self.modules:
|
||||||
self.log.debug("Importing %s" % name)
|
self.log.info("Importing %s" % name)
|
||||||
" attempt to load "
|
" attempt to load "
|
||||||
try:
|
try:
|
||||||
moduleref = __import__(name)
|
moduleref = __import__(name)
|
||||||
|
@ -146,7 +150,7 @@ class PyIRCBot:
|
||||||
|
|
||||||
:param moduleName: Name of the module to import
|
:param moduleName: Name of the module to import
|
||||||
:type moduleName: str"""
|
:type moduleName: str"""
|
||||||
self.log.debug("Deporting %s" % name)
|
self.log.info("Deporting %s" % name)
|
||||||
" unload if necessary "
|
" unload if necessary "
|
||||||
if name in self.moduleInstances:
|
if name in self.moduleInstances:
|
||||||
self.unloadmodule(name)
|
self.unloadmodule(name)
|
||||||
|
@ -368,13 +372,10 @@ class PyIRCBot:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Verified! Return the set.
|
# Verified! Return the set.
|
||||||
ob = type('ParsedCommand', (object,), {})
|
return ParsedCommand(command,
|
||||||
ob.command = command
|
args.split(" "),
|
||||||
ob.args = [] if args=="" else args.split(" ")
|
args,
|
||||||
ob.args_str = args
|
message)
|
||||||
ob.message = message
|
|
||||||
return ob
|
|
||||||
# return (True, command, args, message)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(filepath):
|
def load(filepath):
|
||||||
|
|
Loading…
Reference in New Issue