1
0
Fork 0
chalkbot/main.py

684 lines
30 KiB
Python
Raw Normal View History

2014-02-27 12:05:38 +01:00
#!/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.
#
#
2014-02-28 17:04:08 +01:00
# 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
2014-02-28 17:04:08 +01:00
from select import poll, POLLIN, POLLPRI, POLLOUT,\
POLLERR, POLLHUP, POLLNVAL
2014-03-01 01:25:12 +01:00
from threading import Thread, Event
from urllib.request import urlopen
from urllib.parse import quote_plus
from configobj import ConfigObj
2014-03-01 01:25:12 +01:00
2014-02-28 17:04:08 +01:00
bot = None
logging.basicConfig(format="[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
2014-02-27 12:05:38 +01:00
2014-03-01 01:25:12 +01:00
2014-02-27 12:05:38 +01:00
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"},
mapquest_cfg = {"Active": "0"},
logger = logging.getLogger(logging.basicConfig())
):
self.log = logger
self.nicknames = nicknames
self.ident = ident
self.realname = realname
2014-02-27 12:05:38 +01:00
self.server = server
self.port = int(port)
2014-03-01 01:25:12 +01:00
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)
2014-03-01 01:25:12 +01:00
self.users = users
2014-03-01 01:25:12 +01:00
self.duckduckgo_cfg = duckduckgo_cfg
self.forecast_cfg = forecast_cfg
self.mapquest_cfg = mapquest_cfg
2014-03-01 01:25:12 +01:00
self.user = {"mask":"", "nick":"", "ident":"", "host":""}
2014-03-01 01:25:12 +01:00
2014-02-28 17:04:08 +01:00
self.socket = None
self.recvbuffer = bytearray(1024)
2014-03-01 01:25:12 +01:00
self.recvloop = None
self.parseloop = None
2014-03-01 01:25:12 +01:00
self.die_event = Event()
self.ready = False
self.mode_reply = {}
self.last_msg_ts = time()
2014-02-28 17:04:08 +01:00
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):
2014-02-28 17:04:08 +01:00
"""
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)
2014-02-28 17:04:08 +01:00
p = poll()
try: p.register(self.socket.fileno(), POLLIN)
except Exception as e:
self.log.critical("Fehler, der eigentlich nie auftreten sollte: %s", e)
2014-03-01 01:25:12 +01:00
while not self.die_event.is_set():
2014-02-28 17:04:08 +01:00
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 ---")
2014-02-28 17:04:08 +01:00
# loop for parsing incoming data
def parser(self):
2014-02-28 17:04:08 +01:00
"""
Loop for parsing incoming data
"""
self.log.debug("--- PARSELOOP STARTING ---")
2014-03-01 01:25:12 +01:00
while not self.die_event.is_set():# and self.recvbuffer.endswith(b"\r\n"):# != b"":
if self.recvbuffer.endswith(b"\r\n"):
2014-02-28 17:04:08 +01:00
# 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
2014-03-01 01:25:12 +01:00
origin = {}
2014-02-28 17:04:08 +01:00
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
2014-02-28 17:04:08 +01:00
else:
origin["server"] = prefix
2014-02-27 12:05:38 +01:00
else:
2014-02-28 17:04:08 +01:00
prefix = ""
# parse command
command = head.pop(0)
# parse params
params = head
params.append(larg.strip())
2014-02-28 17:04:08 +01:00
self.log.debug(" > %s" % rawline)
2014-02-28 17:04:08 +01:00
2014-03-01 01:25:12 +01:00
# PING
2014-02-28 17:04:08 +01:00
if command == "PING":
2014-03-01 01:25:12 +01:00
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")
2014-03-01 01:25:12 +01:00
# 001 (RPL_WELCOME)
2014-02-28 17:04:08 +01:00
elif command == "001":
2014-03-01 01:25:12 +01:00
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()
2014-02-28 17:04:08 +01:00
self.ready = True
2014-03-01 01:25:12 +01:00
# 433 (ERR_NICKNAMEINUSE)
2014-02-28 17:04:08 +01:00
elif command == "433":
2014-03-01 01:25:12 +01:00
self.send("NICK %s" % self.nicknames.pop(0))
# KILL
2014-02-28 17:04:08 +01:00
elif command == "KILL":
self.log.warning("Got killed by %s: %s" % (params[0], params[1:]))
self.disconnect(send_quit=False)
2014-03-01 01:25:12 +01:00
else:
2014-03-01 01:34:46 +01:00
sleep(self.parser_wait_time)
self.log.debug("--- PARSELOOP EXITING ---")
2014-03-01 01:25:12 +01:00
### helper functions ###
2014-02-28 17:04:08 +01:00
# 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')
# splits messages into parts with a specific maximal length
def msgsplit(self, msg, length=400):
return [msg[i:i+length] for i in range(0, len(msg), length)]
# checks if a given user may execute a given command
def check_privileges(self, usermask, command, log=True):
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
if log: self.log.info("Unauthorized call of %s from user %s", command, usermask)
2014-02-28 17:04:08 +01:00
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, '")')))
2014-02-28 17:04:08 +01:00
2014-02-27 12:05:38 +01:00
def join(self, channel):
self.exec_on_ready("".join(('self.send("JOIN %s" % "', channel, '")')))
2014-02-28 17:04:08 +01:00
2014-02-27 12:05:38 +01:00
def part(self, channel):
self.exec_on_ready("".join(('self.send("PART %s" % "', channel, '")')))
2014-03-01 01:25:12 +01:00
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"]', ')')))
2014-02-28 17:04:08 +01:00
### handler ###
# bot-command handler
def on_command(self, in_query_type, prefix, origin, source, command, params):
2014-02-28 17:04:08 +01:00
"""
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.
"""
self.log.info("Command from %s: %s", origin["mask"], command)
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\
MapQuest, mq, OpenStreetMap, osm <query> -- Get lat and lon for a place\n\
Fefe [<id>] -- Show a given or the lastest post in Fefes Blog\n\
" % (
self.cmdprefix,
self.user["nick"]
)
if self.check_privileges(origin["mask"], command, log=False):
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":
2014-02-28 17:04:08 +01:00
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"]*100), 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)
if "visibility" in rj["currently"]: 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
# MapQuest, mq, OpenStreetMap, osm <query>
elif command in ("mapquest", "mq", "openstreetmap", "osm") and self.mapquest_cfg["Active"] == "1":
if numparams==0:
return "You didn't ask anything..."
try: rp = urlopen("http://open.mapquestapi.com/nominatim/v1/search?q=%s&format=json"
% quote_plus(" ".join(params)))
except Exception as e:
self.log.error("Error while querying MapQuest: %s" % e)
return "Error while querying MapQuest: %s" % e
if rp.getcode() == 200:
used_fields = (
"display_name",
"lat", "lon"
)
rj = json.loads(str(rp.readall(), "utf-8"))
if len(rj) == 0:
return "No suitable reply from MapQuest for query %s" % " ".join(params)
rj = rj[0]
self.reply(origin, source, "Display Name: %s" % rj["display_name"], in_query_type)
self.reply(origin, source, "Lat: %s" % rj["lat"], in_query_type)
self.reply(origin, source, "Lon: %s" % rj["lon"], in_query_type)
return "(Nominatim Search Courtesy of MapQuest <http://www.mapquest.com/>)"
else:
return "Error while querying MapQuest, got HTTP-Status %i" % rp.getcode()
# Fefe <id>
elif command == "fefe":
if numparams>1:
return "Waddayawannasee? (The one and only optional argument needed is the ID of the blog post. If it is omitted, the last post will be shown.)"
else:
try:
if numparams==1:
rp = urlopen("http://blog.fefe.de/?ts=%s" % quote_plus(params[0]))
else:
rp = urlopen("http://blog.fefe.de/")
except Exception as e:
self.log.error("Error while querying Fefe: %s" % e)
return "Error while querying Fefe: %s" % e
if rp.getcode() == 200:
try:
rpd = str(rp.readall(), "utf-8")
m = re.search(r"<li>(.*?)<\/(?:li|ul)>", "".join(rpd.splitlines())).groups()[0]
print(m+"\n\n")
m = " ".join((re.split(r"<.+?>", m)))[6:]
if len(m)>400:
mmlen = 400
for l in self.msgsplit(m):
print(l)
self.reply(origin, source, l, in_query_type)
except Exception as e:
self.log.warning("Suspectious things happend while handling a response from fefes blog, but it's probably just an incorrect blogpost-id: %s" % e)
return "Nothing matched... (If you're sure your id was correct, see error log)"
### IRC ###
2014-03-01 01:25:12 +01:00
# 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)
2014-03-01 01:25:12 +01:00
# 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)
2014-03-01 01:25:12 +01:00
# 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)
2014-03-01 01:25:12 +01:00
2014-02-27 12:05:38 +01:00
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")
2014-02-27 12:05:38 +01:00
#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__)
2014-02-28 17:04:08 +01:00
global bot
# parse command line and config
2014-02-27 12:05:38 +01:00
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!
2014-02-27 12:05:38 +01:00
if args.action == "start":
2014-02-28 17:04:08 +01:00
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"],
mapquest_cfg = cfg["MapQuest"],
logger = log,
)
2014-02-28 17:04:08 +01:00
bot.connect()
2014-03-01 01:25:12 +01:00
# wait for the bot to become ready
while bot.ready and not bot.die_event.is_set() == False:
2014-03-01 01:34:46 +01:00
sleep(1)
2014-03-01 01:25:12 +01:00
if not bot.die_event.is_set():
# set modes and join channels
bot.set_mode(cfg["Bot"]["Modes"])
for channel in cfg["Network"]["Channels"]:
2014-03-01 01:25:12 +01:00
bot.join(channel)
# while bot is active, do nothing
while not bot.die_event.is_set():
2014-03-01 01:34:46 +01:00
sleep(1)
2014-03-01 01:25:12 +01:00
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)
bot.disconnect("Fehler: %s" % e)
log.debug("--- MAIN EXITING ---")
2014-02-27 12:05:38 +01:00
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]))
2014-02-27 12:05:38 +01:00
return 0
if __name__ == '__main__':
main()