Fanir
f7289a1ac4
implemented usage of bot-nick as cmdprefix changed min time for waiting between channel privmsgs to 0.1sec changed the way pircbot.query() chooses between NOTICE or PRIVMSG some minor fixes and commenting
615 lines
26 KiB
Python
Executable file
615 lines
26 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# main.py
|
|
#
|
|
# Copyright 2014 Fanir <projects@mail.fanir.de>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301, USA.
|
|
#
|
|
#
|
|
|
|
|
|
# SETTINGS CAN BE FOUND AT THE BOTTOM OF THE FILE
|
|
|
|
|
|
import sys, string, socket, re, signal, json, logging
|
|
from random import choice
|
|
from time import sleep, time, strftime, localtime
|
|
from select import poll, POLLIN, POLLPRI, POLLOUT,\
|
|
POLLERR, POLLHUP, POLLNVAL
|
|
from threading import Thread, Event
|
|
from urllib.request import urlopen
|
|
from urllib.parse import quote_plus
|
|
from configobj import ConfigObj
|
|
|
|
bot = None
|
|
|
|
logging.basicConfig(format="[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
class pircbot():
|
|
def __init__( self,
|
|
server,
|
|
nicknames,
|
|
ident = "pircbot",
|
|
realname = "pircbot",
|
|
port = 6667,
|
|
serverpasswd = "",
|
|
encodings = ("utf-8", "latin-1"),
|
|
query_type = "",
|
|
command_prefix = "!",
|
|
msg_wait_time = 0,
|
|
parser_wait_time = 0.1,
|
|
users = "",
|
|
duckduckgo_cfg = {"Active": "0"},
|
|
forecast_cfg = {"Active": "0"},
|
|
logger = logging.getLogger(logging.basicConfig())
|
|
):
|
|
self.log = logger
|
|
|
|
|
|
self.nicknames = nicknames
|
|
self.ident = ident
|
|
self.realname = realname
|
|
|
|
self.server = server
|
|
self.port = int(port)
|
|
self.serverpasswd = serverpasswd
|
|
self.encodings = encodings
|
|
|
|
self.query_type = query_type.upper()
|
|
self.cmdprefix = command_prefix
|
|
self.msg_wait_time = float(msg_wait_time)
|
|
if self.msg_wait_time < 0.1:
|
|
self.log.info("msg_wait_time ist zu klein, nutze 0.1 Sekunden")
|
|
self.msg_wait_time = 0.1
|
|
self.parser_wait_time = float(parser_wait_time)
|
|
|
|
self.users = users
|
|
|
|
self.duckduckgo_cfg = duckduckgo_cfg
|
|
self.forecast_cfg = forecast_cfg
|
|
|
|
|
|
self.user = {"mask":"", "nick":"", "ident":"", "host":""}
|
|
|
|
self.socket = None
|
|
self.recvbuffer = bytearray(1024)
|
|
|
|
self.recvloop = None
|
|
self.parseloop = None
|
|
|
|
self.die_event = Event()
|
|
self.ready = False
|
|
|
|
self.mode_reply = {}
|
|
|
|
self.last_msg_ts = time()
|
|
|
|
def connect(self):
|
|
# connect
|
|
self.socket = socket.socket()
|
|
self.log.debug("--- SOCKET OPENING ---")
|
|
try:
|
|
self.socket.connect((self.server, self.port))
|
|
except Exception as e:
|
|
self.log.critical("Fehler beim Verbinden: %s", e)
|
|
self.disconnect(send_quit=False)
|
|
|
|
# start getting data
|
|
try:
|
|
self.recvloop = Thread(target=self.recv, name="recvloop")
|
|
self.recvloop.start()
|
|
except Exception as e:
|
|
self.log.critical("Fehler beim Starten des Empfangs von Daten vom IRC: %s", e)
|
|
self.disconnect(send_quit=False)
|
|
|
|
try:
|
|
# optionally send a server password
|
|
if self.serverpasswd != "": self.send("PASS %s" % self.serverpasswd)
|
|
# get a nick
|
|
self.send("NICK %s" % self.nicknames.pop(0))
|
|
# set user data
|
|
self.send("USER %s 0 * :%s" % (self.ident, self.realname))
|
|
except Exception as e:
|
|
self.log.critical("Fehler beim IRC-Handshake: %s", e)
|
|
self.disconnect(send_quit=False)
|
|
|
|
# implements irc command QUIT and (more or less) clean exiting
|
|
def disconnect(self, reason="", send_quit=True):
|
|
if send_quit:
|
|
try: self.send("QUIT :%s" % reason)
|
|
except Exception as e: self.log.critical("Fehler beim Senden der QUIT-Nachricht: %s", e)
|
|
sleep(1)
|
|
self.die_event.set()
|
|
ctr = 0
|
|
while self.recvloop.is_alive() and self.parseloop.is_alive() and ctr < 15:
|
|
ctr += 1
|
|
sleep(1)
|
|
self.log.debug("--- SOCKET CLOSING ---")
|
|
try:
|
|
self.socket.shutdown(socket.SHUT_RDWR)
|
|
self.socket.close()
|
|
except Exception as e: self.log.warning("Fehler beim Schließen des Sockets: %s", e)
|
|
|
|
|
|
### threaded functions ###
|
|
|
|
# loop for recieving data from irc
|
|
def recv(self):
|
|
"""
|
|
Loop for reciving data
|
|
"""
|
|
self.log.debug("--- RECVLOOP STARTING ---")
|
|
try: self.parseloop = Thread(target=self.parser, name="parser")
|
|
except Exception as e:
|
|
self.log.error("Fehler beim Vorbereiten des Threads zum Parsen von Nachrichten von IRC: %s", e)
|
|
p = poll()
|
|
try: p.register(self.socket.fileno(), POLLIN)
|
|
except Exception as e:
|
|
self.log.critical("Fehler, der eigentlich nie auftreten sollte: %s", e)
|
|
while not self.die_event.is_set():
|
|
ap = p.poll(1000)
|
|
if (self.socket.fileno(), POLLIN) in ap:
|
|
try: self.recvbuffer.extend(self.socket.recv(1024))
|
|
except Exception as e:
|
|
self.log.critical("Konnte keine Daten vom IRC lesen. Verbindung tot? Fehler: %s", e)
|
|
if not self.parseloop.is_alive():
|
|
try:
|
|
self.parseloop = Thread(target=self.parser, name="parser")
|
|
self.parseloop.start()
|
|
except Exception as e:
|
|
self.log.critical("Fehler beim Starten des Threads zum Parsen von Nachrichten vom IRC: %s", e)
|
|
self.log.debug("--- RECVLOOP EXITING ---")
|
|
|
|
# loop for parsing incoming data
|
|
def parser(self):
|
|
"""
|
|
Loop for parsing incoming data
|
|
"""
|
|
self.log.debug("--- PARSELOOP STARTING ---")
|
|
while not self.die_event.is_set():# and self.recvbuffer.endswith(b"\r\n"):# != b"":
|
|
if self.recvbuffer.endswith(b"\r\n"):
|
|
# get and decode line from buffer
|
|
rawline, _, self.recvbuffer = self.recvbuffer.partition(b"\r\n")
|
|
rawline = self.decode(rawline)
|
|
# prepare line
|
|
line = rawline.split(" :", 1)
|
|
head = line[0].lstrip("\x00").split(" ")
|
|
larg = line[1] if len(line)>1 else ""
|
|
# parse prefix
|
|
origin = {}
|
|
if head[0].startswith(":"):
|
|
prefix = head.pop(0)[1:]
|
|
if "@" in prefix:
|
|
origin["nick"], origin["ident"], origin["host"] = re.match(r"(\S+)!(\S+)@(\S+)", prefix).groups()
|
|
origin["mask"] = prefix
|
|
else:
|
|
origin["server"] = prefix
|
|
else:
|
|
prefix = ""
|
|
# parse command
|
|
command = head.pop(0)
|
|
# parse params
|
|
params = head
|
|
params.append(larg.strip())
|
|
|
|
self.log.debug(" > %s" % rawline)
|
|
|
|
# PING
|
|
if command == "PING":
|
|
self.send("PONG %s" % params[0])
|
|
# PRIVMSG and NOTICE
|
|
elif command == "PRIVMSG" or command == "NOTICE" and self.ready:
|
|
args = []
|
|
for v in params[1].split(" "):
|
|
if v!="": args.append(v)
|
|
if len(args)>1 and args[0][0:len(self.user["nick"])] == self.user["nick"]:
|
|
args.pop(0)
|
|
args[0] = "".join((self.cmdprefix, args[0]))
|
|
if args[0][0] == self.cmdprefix:
|
|
args[0] = args[0][1:]
|
|
rp = self.on_command(command, prefix, origin, params[0], args[0].lower(), args[1:])
|
|
if rp not in (None, ""):
|
|
self.reply(origin, params[0], rp, command)
|
|
# 221 (RPL_UMODEIS)
|
|
elif command == "221" and self.mode_reply is not {}:
|
|
self.query(self.mode_reply["to"], "Modes are %s" % params[1], self.mode_reply["type"])
|
|
self.mode_reply = {}
|
|
# INVITE
|
|
elif command == "INVITE":
|
|
if self.check_privileges(origin["mask"], command):
|
|
self.join(params[1])
|
|
else:
|
|
self.query(origin["nick"], "You can not force me to do that!", "NOTICE")
|
|
# 001 (RPL_WELCOME)
|
|
elif command == "001":
|
|
self.user["mask"] = re.search(r" (\S+!\S+@\S+)$", rawline.split(" :", 1)[1]).groups()[0]
|
|
self.user["nick"], self.user["ident"], self.user["host"] = re.match(r"(\S+)!(\S+)@(\S+)", self.user["mask"]).groups()
|
|
self.ready = True
|
|
# 433 (ERR_NICKNAMEINUSE)
|
|
elif command == "433":
|
|
self.send("NICK %s" % self.nicknames.pop(0))
|
|
# KILL
|
|
elif command == "KILL":
|
|
self.log.warning("Got killed by %s: %s" % (params[0], params[1:]))
|
|
self.disconnect(send_quit=False)
|
|
else:
|
|
sleep(self.parser_wait_time)
|
|
self.log.debug("--- PARSELOOP EXITING ---")
|
|
|
|
|
|
### helper functions ###
|
|
|
|
# encodes data for irc
|
|
def encode(self, textstring):
|
|
for codec in self.encodings:
|
|
try: return textstring.encode(codec)
|
|
except UnicodeDecodeError: continue
|
|
return textstring.encode(self.encodings[0], 'ignore')
|
|
|
|
# decodes data from irc
|
|
def decode(self, textstring):
|
|
for codec in self.encodings:
|
|
try: return textstring.decode(codec)
|
|
except UnicodeDecodeError: continue
|
|
return textstring.decode(self.encodings[0], 'ignore')
|
|
|
|
|
|
# checks if a given user may execute a given command
|
|
def check_privileges(self, usermask, command):
|
|
for user, privs in self.users.items():
|
|
if re.search(user, usermask, re.IGNORECASE) != None:
|
|
if command.lower() in privs or "*" in privs:
|
|
return True
|
|
return False
|
|
|
|
|
|
# checks if s is a valid channel name
|
|
def is_channel(self, s):
|
|
return (True if s[0] in ('&', '#', '!') else False)
|
|
|
|
|
|
def exec_on_ready(self, command, retry_times=-1, interval=1):
|
|
if command.startswith("self."):
|
|
if retry_times == -1:
|
|
while not self.ready: sleep(interval)
|
|
else:
|
|
cnt = 0
|
|
while not self.ready and cnt<retry_times: sleep(interval)
|
|
if not self.ready:
|
|
self.log.warning("Connection did not get ready in time (%sx %s seconds), \"%s\" not executed" % (retry_times, interval, command))
|
|
return True
|
|
exec("ret = %s" % command)
|
|
return False
|
|
else:
|
|
self.log.critical("exec_on_ready() called with an invalid command: %s" % command)
|
|
return True
|
|
|
|
def exec_retry(self, command, times=5, interval=1, wait_for_ready=True):
|
|
if command.startswith("self."):
|
|
if wait_for_ready:
|
|
while not self.ready: sleep(interval)
|
|
cnt = 0
|
|
exec("while %s and cnt<times: sleep(interval)" % command)
|
|
else: self.log.critical("exec_retry() called with an invalid command.")
|
|
|
|
|
|
def send(self, data):
|
|
self.log.debug("< %s" % data)
|
|
try: self.socket.send(self.encode("".join((data, "\r\n"))))
|
|
except Exception as e:
|
|
self.log.critical("Fehler beim Senden von Daten: %s", e)
|
|
self.disconnect(send_quit=False)
|
|
|
|
# decides whether to reply to user or to channel
|
|
def reply(self, origin, source, msg, in_query_type):
|
|
if self.is_channel(source):
|
|
self.chanmsg(source, msg)
|
|
else:
|
|
self.query(origin["nick"], msg, in_query_type)
|
|
|
|
|
|
### irc command wrapper ###
|
|
|
|
# replies to channel by PRIVMSG
|
|
def chanmsg(self, channel, msg):
|
|
while self.last_msg_ts + self.msg_wait_time > time():
|
|
sleep(0.1)
|
|
self.last_msg_ts = time()
|
|
self.send("".join(("PRIVMSG ", channel, " :", msg)))
|
|
|
|
# replies to user by NOTICE or PRIVMSG
|
|
def query(self, nick, msg, in_query_type="", force_query_type=""):
|
|
self.send("".join((force_query_type if force_query_type!="" else
|
|
(self.query_type if self.query_type!="" else
|
|
(in_query_type if in_query_type!="" else
|
|
"PRIVMSG")), " ", nick, " :", msg)))
|
|
|
|
def nick(self, nick):
|
|
self.exec_on_ready("".join(('self.send("JOIN %s" % "', channel, '")')))
|
|
|
|
def join(self, channel):
|
|
self.exec_on_ready("".join(('self.send("JOIN %s" % "', channel, '")')))
|
|
|
|
def part(self, channel):
|
|
self.exec_on_ready("".join(('self.send("PART %s" % "', channel, '")')))
|
|
|
|
def set_mode(self, modes, target=""):
|
|
self.exec_on_ready("".join(('self.send("MODE %s :%s" % (', ''.join(('"', target, '"')) if target != "" else 'self.user["nick"]', ', "', modes, '"))')))
|
|
|
|
def get_modes(self, target=""):
|
|
self.exec_on_ready("".join(('self.send("MODE %s" % ', ''.join(('"', target, '"')) if target != "" else 'self.user["nick"]', ')')))
|
|
|
|
### handler ###
|
|
|
|
# bot-command handler
|
|
def on_command(self, in_query_type, prefix, origin, source, command, params):
|
|
"""
|
|
Executed when getting a PRIVMSG starting with self.cmdprefix
|
|
Prefix contains the optional prefix of the raw line.
|
|
Origin is a map holding the parsed prefix, containing
|
|
"nick", "ident" and "host" for users and "server" for servers.
|
|
Source is the first parameter after the irc-command, specifying
|
|
the channel or user, from where the line comes.
|
|
Command is the command for the bot.
|
|
Params contains a list of originally space separated parameters.
|
|
"""
|
|
numparams = len(params)
|
|
### INTERNAL ###
|
|
# help [me]
|
|
if command == "help":
|
|
if numparams == 1 and params[0].startswith("me"):
|
|
rply = "\
|
|
Nope, you're on your own."
|
|
else:
|
|
rply = "\
|
|
Command Prefix is \"%s\" or \"%s\"\n\
|
|
\n\
|
|
Commands:\n\
|
|
hello [<...>]\n\
|
|
say <text>\n\
|
|
choose <choice1>, <choice2>[, <choice3>[, ...]] -- Let the bot decide!\n\
|
|
DuckDuckGo, ddg <query> -- Ask the DuckDuckGo Instant Answer API\n\
|
|
Forecast, fc <query> -- Query Forecast.io\n\
|
|
" % (
|
|
self.cmdprefix,
|
|
self.user["nick"]
|
|
)
|
|
if self.check_privileges(origin["mask"], command):
|
|
rply += " \n\
|
|
~ For the aristocrats ~\n\
|
|
join <channel>\n\
|
|
part <channel>\n\
|
|
mode ±<modes>\n\
|
|
die [<quitmsg>]"
|
|
for line in rply.splitlines(): self.query(origin["nick"], line[20:], force_query_type="PRIVMSG")
|
|
|
|
# hello [<...>]
|
|
elif command == "hello":
|
|
greeting = "".join(("Hi " + origin["nick"] +"!"))
|
|
return greeting
|
|
# say <text>
|
|
elif command == "say":
|
|
return " ".join(params)
|
|
# choose <choice1>, <choice2>[, <choice3>[, ...]]
|
|
elif command == "choose":
|
|
choices = " ".join(params).split(", ")# if numparams>1 else params.split(", ")
|
|
if choices[0] == "":
|
|
return "Whaddayawant? (Seperated by \", \")"
|
|
elif len(choices) == 1:
|
|
return "Such a difficult question... I don't know..."
|
|
else:
|
|
return choice(choices)
|
|
|
|
### EXTERNAL ###
|
|
# DuckDuckGo, ddg <query>
|
|
elif command in ("duckduckgo", "ddg") and self.duckduckgo_cfg["Active"] == "1":
|
|
if numparams==0:
|
|
return "You didn't ask anything..."
|
|
try: rp = urlopen("https://api.duckduckgo.com/?q=%s&format=json&no_html=1&no_redirect=1&t=pircbot:chalkbot"
|
|
% quote_plus(" ".join(params)))
|
|
except Exception as e:
|
|
self.log.error("Error while querying DuckDuckGo: %s" % e)
|
|
return "Error while querying DuckDuckGo: %s" % e
|
|
if rp.getcode() == 200:
|
|
used_fields = (
|
|
"Heading", "AbstractText", "AbstractSource", "AbstractURL",
|
|
"AnswerType", "Answer",
|
|
"Definition", "DefinitionSource", "DefinitionURL",
|
|
)
|
|
rj = json.loads(str(rp.readall(), "utf-8"))
|
|
empty_field_counter = 0
|
|
for elem in [v for v in used_fields if v in rj]:
|
|
if rj[elem] not in ("", []):
|
|
self.reply(origin, source, "%s: %s" % (elem, rj[elem]), in_query_type)
|
|
else:
|
|
empty_field_counter+=1
|
|
if empty_field_counter == len(used_fields):
|
|
return "No suitable reply from DuckDuckGo for query %s" % " ".join(params)
|
|
else:
|
|
return "(Results from DuckDuckGo <https://duckduckgo.com>)"
|
|
else:
|
|
return "Error while querying DuckDuckGo, got HTTP-Status %i" % rp.getcode()
|
|
# Forecast, fc <query>
|
|
elif command in ("forecast", "fc") and self.forecast_cfg["Active"] == "1":
|
|
if numparams==2:
|
|
try: rp = urlopen("https://api.forecast.io/forecast/%s/%s?units=si&exclude=minutely,hourly,daily"
|
|
% (self.forecast_cfg["ApiKey"], quote_plus(",".join(params))))
|
|
except Exception as e:
|
|
self.log.error("Error while querying Forecast.io: %s" % e)
|
|
return "Error while querying Forecast.io: %s" % e
|
|
if rp.getcode() == 200:
|
|
rj = json.loads(str(rp.readall(), "utf-8"))
|
|
self.reply(origin, source,
|
|
strftime("CURRENTLY (%d.%m.%Y %H:%M:%S)", localtime(rj["currently"]["time"])), in_query_type)
|
|
self.reply(origin, source,
|
|
"Summary: %s" % rj["currently"]["summary"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Temperature: %s °C" % rj["currently"]["temperature"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Apparent Temperature: %s °C" % rj["currently"]["apparentTemperature"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Dew Point: %s °C" % rj["currently"]["dewPoint"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Wind Speed: %s m/s" % rj["currently"]["windSpeed"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Cloud Cover: %s %%" % rj["currently"]["cloudCover"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Precipitation Probability: %s %%" % (rj["currently"]["precipProbability"]*100), in_query_type)
|
|
if "precipIntensity" in rj["currently"]: self.reply(origin, source,
|
|
"Precipitation Intensity: %s mm/h" % rj["currently"]["precipIntensity"], in_query_type)
|
|
if "precipType" in rj["currently"]: self.reply(origin, source,
|
|
"Precipitation Type: %s" % rj["currently"]["precipType"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Visibility: %s km" % rj["currently"]["visibility"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Humidity: %s %%" % (rj["currently"]["humidity"]*100), in_query_type)
|
|
self.reply(origin, source,
|
|
"Pressure: %s hPa" % rj["currently"]["pressure"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Ozone: %s DU" % rj["currently"]["ozone"], in_query_type)
|
|
if "nearestStormDistance" in rj["currently"]: self.reply(origin, source,
|
|
"Nearest Storm Distance: %s km" % rj["currently"]["nearestStormDistance"], in_query_type)
|
|
self.reply(origin, source,
|
|
"Sources: %s" % ", ".join(rj["flags"]["sources"]), in_query_type)
|
|
return "(Powered by Forecast <http://forecast.io/>)"
|
|
else:
|
|
return "Error while querying Forecast.io, got HTTP-Status %i" % rp.getcode()
|
|
else:
|
|
return "Usage: %s <lat> <lon>" % command
|
|
|
|
### IRC ###
|
|
# join <channel>
|
|
elif command == "join":
|
|
if numparams>0:
|
|
if self.check_privileges(origin["mask"], command): self.join(params[0])
|
|
else: self.query(origin["nick"], "You cannot do that!", in_query_type)
|
|
# part <channel>
|
|
elif command == "part":
|
|
if numparams>0:
|
|
if self.check_privileges(origin["mask"], command): self.part(params[0])
|
|
else: self.query(origin["nick"], "You cannot do that!", in_query_type)
|
|
# mode ±<modes>
|
|
elif command == "mode":
|
|
if self.check_privileges(origin["mask"], command):
|
|
if numparams==0:
|
|
self.mode_reply["to"] = origin["nick"]
|
|
self.mode_reply["type"] = in_query_type
|
|
self.get_modes()
|
|
else:
|
|
self.set_mode(" ".join(params) if numparams>0 else params)
|
|
else: self.query(origin["nick"], "You cannot do that!", in_query_type)
|
|
|
|
# die [<quitmsg>]
|
|
elif command == "die":
|
|
if self.check_privileges(origin["mask"], command):
|
|
self.disconnect("".join(params) if numparams>0 else "".join((origin["nick"], " shot me, dying now... Bye...")))
|
|
else: self.query(origin["nick"], "Go die yourself!", in_query_type)
|
|
else:
|
|
replies = [
|
|
("What? \"%s\" is not a command!", 15),
|
|
("%s? What's that?", 3),
|
|
("Sorry, I don't know how to %s...", 1)
|
|
]
|
|
self.query(origin["nick"], choice([val for val, cnt in replies for i in range(cnt)]) % (command), in_query_type)
|
|
|
|
|
|
|
|
def parseargs():
|
|
import argparse
|
|
p = argparse.ArgumentParser(
|
|
description = "guess what? i think my desc is still missing!")
|
|
p.add_argument("action",
|
|
default = "help",
|
|
choices = ["start", "stop", "checkconf"],
|
|
help = "What to do?")
|
|
p.add_argument("--loglevel", "-l",
|
|
choices = ["critical", "error", "warning", "info", "debug"],
|
|
help = "Verbosity of logging")
|
|
#p.add_argument("--daemon", "-d", type = bool, choices = [1, 0], default=1, help="Daemonize, Default: 1")
|
|
return p.parse_args()
|
|
|
|
def main():
|
|
# get the logging-module logger
|
|
log = logging.getLogger(__name__)
|
|
|
|
global bot
|
|
|
|
# parse command line and config
|
|
args = parseargs()
|
|
cfg = ConfigObj("bot.conf")
|
|
|
|
# set the loglevel
|
|
nll = getattr(logging, cfg["Behavior"]["Loglevel"].upper(), None)
|
|
if not isinstance(nll, int): raise ValueError('Invalid log level: %s' % cfg["Behavior"]["Loglevel"])
|
|
if args.loglevel != None: nll = getattr(logging, args.loglevel.upper(), None)
|
|
if not isinstance(nll, int):
|
|
raise ValueError('Invalid log level: %s' % args.loglevel)
|
|
log.setLevel(nll)
|
|
|
|
# do awesome stuff!
|
|
if args.action == "start":
|
|
try:
|
|
bot = pircbot(
|
|
nicknames = cfg["Bot"]["Nicknames"],
|
|
ident = cfg["Bot"]["Ident"],
|
|
realname = cfg["Bot"]["Realname"],
|
|
server = cfg["Network"]["Server"],
|
|
port = cfg["Network"]["Port"],
|
|
serverpasswd = cfg["Network"]["ServerPasswd"],
|
|
encodings = cfg["Network"]["Encodings"],
|
|
query_type = cfg["Behavior"]["QueryType"],
|
|
command_prefix = cfg["Behavior"]["CommandPrefix"],
|
|
msg_wait_time = cfg["Behavior"]["MsgWaitTime"],
|
|
parser_wait_time = cfg["Behavior"]["ParserWaitTime"],
|
|
users = cfg["Permissions"],
|
|
duckduckgo_cfg = cfg["DuckDuckGo"],
|
|
forecast_cfg = cfg["Forecast.io"],
|
|
logger = log,
|
|
)
|
|
bot.connect()
|
|
# wait for the bot to become ready
|
|
while bot.ready and not bot.die_event.is_set() == False:
|
|
sleep(1)
|
|
|
|
if not bot.die_event.is_set():
|
|
# set modes and join channels
|
|
bot.set_mode(cfg["Bot"]["Modes"])
|
|
for channel in cfg["Network"]["Channels"]:
|
|
bot.join(channel)
|
|
|
|
# while bot is active, do nothing
|
|
while not bot.die_event.is_set():
|
|
sleep(1)
|
|
except KeyboardInterrupt:
|
|
log.info("Got Ctrl-C, dying now...")
|
|
bot.disconnect("Ouch! Got shot by Ctrl-C, dying now... See you!")
|
|
except Exception as e:
|
|
log.exception("Fehler: %s", e)
|
|
log.debug("--- MAIN EXITING ---")
|
|
elif args.action == "stop": print("nope!")
|
|
elif args.action == "checkconf":
|
|
for section, settings in cfg.items():
|
|
print("".join(("[", section, "]")))
|
|
for e in settings:
|
|
print("%20s : %s" % (e, settings[e]))
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|