diff --git a/bot.conf.example b/bot.conf.example index 5bbf7d6..03defdd 100644 --- a/bot.conf.example +++ b/bot.conf.example @@ -47,7 +47,8 @@ Modes = +iB [Permissions] # List of users (hostmasks as regex) and the commands they are allowed to execute. # * can be used instead of commands to allow every command. You should append a trailing comma. -# All command names should be lowercase. +# All command names should be lowercase. Add "help" as a command if you want the user +# to see all aviable commands in the help message (is automatically included with "*"). yournickname\!yourusername@.* = *, \!@some other user = join, part, invite .*\!murderer@(localhost|127(\.0){2}\.1) = die, diff --git a/main.py b/main.py index eb056d5..5ed8355 100755 --- a/main.py +++ b/main.py @@ -28,7 +28,7 @@ import sys, string, socket, re, signal, json, logging from random import choice -from time import sleep, time +from time import sleep, time, strftime, localtime from select import poll, POLLIN, POLLPRI, POLLOUT,\ POLLERR, POLLHUP, POLLNVAL from threading import Thread, Event @@ -59,6 +59,9 @@ class pircbot(): forecast_cfg = {"Active": "0"}, logger = logging.getLogger(logging.basicConfig()) ): + self.log = logger + + self.nicknames = nicknames self.ident = ident self.realname = realname @@ -71,6 +74,9 @@ class pircbot(): 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 @@ -78,10 +84,8 @@ class pircbot(): self.duckduckgo_cfg = duckduckgo_cfg self.forecast_cfg = forecast_cfg - self.log = logger - - self.user = {} + self.user = {"mask":"", "nick":"", "ident":"", "host":""} self.socket = None self.recvbuffer = bytearray(1024) @@ -210,11 +214,15 @@ class pircbot(): if command == "PING": self.send("PONG %s" % params[0]) # PRIVMSG and NOTICE - elif command == "PRIVMSG" or command == "NOTICE": - if params[1][0] == self.cmdprefix: - args = [] - for v in params[1][1:].split(" "): - if v!="": args.append(v) + 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) @@ -326,8 +334,11 @@ class pircbot(): self.send("".join(("PRIVMSG ", channel, " :", msg))) # replies to user by NOTICE or PRIVMSG - def query(self, nick, msg, in_query_type): - self.send("".join((self.query_type if self.query_type!="" else in_query_type, " ", nick, " :", msg))) + 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, '")'))) @@ -359,14 +370,43 @@ class pircbot(): Params contains a list of originally space separated parameters. """ numparams = len(params) - # hello - if command == "hello": + ### 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 \n\ + choose , [, [, ...]] -- Let the bot decide!\n\ + DuckDuckGo, ddg -- Ask the DuckDuckGo Instant Answer API\n\ + Forecast, fc -- Query Forecast.io\n\ + " % ( + self.cmdprefix, + self.user["nick"] + ) + if self.check_privileges(origin["mask"], command): + rply += " \n\ + ~ For the aristocrats ~\n\ + join \n\ + part \n\ + mode ±\n\ + die []" + 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 + # say elif command == "say": return " ".join(params) - # choose + # choose , [, [, ...]] elif command == "choose": choices = " ".join(params).split(", ")# if numparams>1 else params.split(", ") if choices[0] == "": @@ -376,6 +416,7 @@ class pircbot(): else: return choice(choices) + ### EXTERNAL ### # DuckDuckGo, ddg elif command in ("duckduckgo", "ddg") and self.duckduckgo_cfg["Active"] == "1": if numparams==0: @@ -393,7 +434,7 @@ class pircbot(): ) rj = json.loads(str(rp.readall(), "utf-8")) empty_field_counter = 0 - for elem in [rj for rj in used_fields]: + 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: @@ -404,14 +445,55 @@ class pircbot(): return "(Results from DuckDuckGo )" else: return "Error while querying DuckDuckGo, got HTTP-Status %i" % rp.getcode() - # Forecast, fc, weather - elif command in ("weather", "forecast", "fc") and self.forecast_cfg["Active"] == "1": + # Forecast, fc + elif command in ("forecast", "fc") and self.forecast_cfg["Active"] == "1": if numparams==2: - - return "(Powered by Forecast )" + 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 )" + else: + return "Error while querying Forecast.io, got HTTP-Status %i" % rp.getcode() else: return "Usage: %s " % command + ### IRC ### # join elif command == "join": if numparams>0: