Add listener object
parent
3914db4685
commit
45029d9525
90
irc.py
90
irc.py
|
@ -8,18 +8,23 @@ class IRC:
|
||||||
|
|
||||||
debug = False
|
debug = False
|
||||||
|
|
||||||
def run(self, handler, conf):
|
def run(self, listen_hook, conf):
|
||||||
"""A routine that connects to a server, joins channels, and initialises
|
"""A routine that connects to a server, joins channels, and initialises
|
||||||
the request handler in a loop."""
|
the request listener in a loop."""
|
||||||
self.connect(conf.server, conf.bot_nick)
|
# Conf settings needed by listen(), also used by disconnect()
|
||||||
|
self.bot_nick = conf.bot_nick
|
||||||
|
self.admin_user = conf.admin_user
|
||||||
|
self.admin_code = conf.admin_code
|
||||||
|
self.connect(conf.server, self.bot_nick)
|
||||||
self.join_channels(conf.channels)
|
self.join_channels(conf.channels)
|
||||||
while 1:
|
while 1:
|
||||||
sleep(2)
|
sleep(2)
|
||||||
data = self.receive(debug=conf.debug)
|
data = self.receive()
|
||||||
self.keep_alive(data)
|
self.keepalive(data)
|
||||||
self.msg = self.parse_msg(data, conf.req_prefix)
|
self.msg = self.parse(data, conf.req_prefix)
|
||||||
for c in conf.channels:
|
for c in conf.channels:
|
||||||
handler(self.msg, c)
|
# Pass in a context dict for handlers
|
||||||
|
listen_hook({"msg": self.msg, "listen_chan": c})
|
||||||
|
|
||||||
def connect(self, server, bot_nick):
|
def connect(self, server, bot_nick):
|
||||||
"""Connect to the server and sends user/nick information."""
|
"""Connect to the server and sends user/nick information."""
|
||||||
|
@ -32,10 +37,24 @@ class IRC:
|
||||||
bot_nick + " " + bot_nick)
|
bot_nick + " " + bot_nick)
|
||||||
self.send("NICK", bot_nick)
|
self.send("NICK", bot_nick)
|
||||||
|
|
||||||
def disconnect(self, resp_msg, quit_msg, admin_user):
|
def disconnect(self, resp_msg, quit_msg):
|
||||||
"""Notify the admin user and disconnect from the server."""
|
"""Notify the admin user and disconnect from the server."""
|
||||||
self.send("PRIVMSG", resp_msg, recvr=admin_user)
|
self.send("PRIVMSG", resp_msg, recvr=self.admin_user)
|
||||||
self.send("QUIT", ":" + quit_msg)
|
self.send("QUIT", ":" + quit_msg)
|
||||||
|
# Currently only one server per app instance is supported, so
|
||||||
|
# disconnect also exits the app
|
||||||
|
exit("Shutting down ...")
|
||||||
|
|
||||||
|
def keepalive(self, line):
|
||||||
|
"""Stay connected to a server by responding to server pings."""
|
||||||
|
if "PING" in line:
|
||||||
|
self.send("PONG", ":" + line.split(":", 2)[1].split(" ", 1)[0])
|
||||||
|
|
||||||
|
def join_channels(self, channels):
|
||||||
|
"""Join channels given a list of channel names."""
|
||||||
|
for c in channels:
|
||||||
|
if c.strip() != "" or c.strip() != "#":
|
||||||
|
self.send("JOIN", c)
|
||||||
|
|
||||||
def send(self, command, text, *args, **kwargs):
|
def send(self, command, text, *args, **kwargs):
|
||||||
"""Send messages given the IRC command and text. Optionally specify a
|
"""Send messages given the IRC command and text. Optionally specify a
|
||||||
|
@ -54,25 +73,14 @@ class IRC:
|
||||||
bs.decode("utf-8").strip() + "`")
|
bs.decode("utf-8").strip() + "`")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def receive(self, *args, **kwargs):
|
def receive(self):
|
||||||
"""Get messages from the connected socket."""
|
"""Get messages from the connected socket."""
|
||||||
data = self.sock.recv(2040).decode("utf-8").strip("\r\n")
|
data = self.sock.recv(2040).decode("utf-8").strip("\r\n")
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print("[debug][recv] " + data)
|
print("[debug][recv] " + data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def keep_alive(self, line):
|
def parse(self, line, req_prefix):
|
||||||
"""Stay connected to a server by responding to server pings."""
|
|
||||||
if "PING" in line:
|
|
||||||
self.send("PONG", ":" + line.split(":", 2)[1].split(" ", 1)[0])
|
|
||||||
|
|
||||||
def join_channels(self, channels):
|
|
||||||
"""Join channels given a list of channel names."""
|
|
||||||
for c in channels:
|
|
||||||
if c.strip() != "" or c.strip() != "#":
|
|
||||||
self.send("JOIN", c)
|
|
||||||
|
|
||||||
def parse_msg(self, line, req_prefix):
|
|
||||||
"""Using received data from a socket, extract the request, the nick and
|
"""Using received data from a socket, extract the request, the nick and
|
||||||
username of the requester, the channel where the request originated and
|
username of the requester, the channel where the request originated and
|
||||||
return a dictionary of values."""
|
return a dictionary of values."""
|
||||||
|
@ -85,3 +93,41 @@ class IRC:
|
||||||
data["nick"] = line.split("!~", 1)[0][1:]
|
data["nick"] = line.split("!~", 1)[0][1:]
|
||||||
data["user"] = line.split("!~", 2)[1][0:].split("@", 1)[0]
|
data["user"] = line.split("!~", 2)[1][0:].split("@", 1)[0]
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def listen(self, context, trigger, handler, *args, **kwargs):
|
||||||
|
"""Listen for a trigger and call the handler. It takes a context
|
||||||
|
dictionary (to determine the channel and recipient), trigger string and
|
||||||
|
corresponding handler function. Optional flags (chan, query, admin) can
|
||||||
|
be used to specify whether a trigger is active in channels or by
|
||||||
|
/msg. e.g. `chan=False, query=True` to make a trigger query-only. By
|
||||||
|
default, it will listen in both channels and private messages."""
|
||||||
|
in_chan = kwargs.get("chan", True)
|
||||||
|
in_query = kwargs.get("query", True)
|
||||||
|
in_admin = kwargs.get("admin", False)
|
||||||
|
# Admin requests are query/pm only
|
||||||
|
if in_admin:
|
||||||
|
in_chan = False
|
||||||
|
msg = context["msg"]
|
||||||
|
channel = context["listen_chan"]
|
||||||
|
# Responses are sent via pm to the user by default, while requests made
|
||||||
|
# in a channel are sent to the channel. While it's possible to override
|
||||||
|
# the recvr, it usually easier to enable a trigger in the same location
|
||||||
|
# where the response will be sent.
|
||||||
|
context["recvr"] = msg["user"]
|
||||||
|
|
||||||
|
if msg["req"] == trigger:
|
||||||
|
# Respond only in the channel where the request was made
|
||||||
|
if in_chan and channel == msg["req_chan"]:
|
||||||
|
context["recvr"] = msg["req_chan"]
|
||||||
|
handler(context)
|
||||||
|
# Respond to query/pm
|
||||||
|
elif in_query and msg["req_chan"] == self.bot_nick:
|
||||||
|
handler(context)
|
||||||
|
# Respond only to the admin user
|
||||||
|
elif in_admin and msg["user"].lower() == \
|
||||||
|
self.admin_user.lower() and self.admin_code in msg["req"]:
|
||||||
|
handler(context)
|
||||||
|
|
||||||
|
def reply(self, cxt, text):
|
||||||
|
"""Alias of send() with PRIVMSG command preset."""
|
||||||
|
self.send("PRIVMSG", text, recvr=cxt["recvr"])
|
||||||
|
|
56
ramen.py
56
ramen.py
|
@ -1,4 +1,3 @@
|
||||||
from sys import exit
|
|
||||||
from random import randint
|
from random import randint
|
||||||
|
|
||||||
import config as cfg
|
import config as cfg
|
||||||
|
@ -9,39 +8,24 @@ class Ramen:
|
||||||
"""Requests with a ramen theme."""
|
"""Requests with a ramen theme."""
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
"""Instantiate an IRC object and pass in the request handler."""
|
"""Instantiate an IRC object and attach the listeners."""
|
||||||
self.irc = IRC()
|
self.irc = IRC()
|
||||||
self.irc.debug = cfg.debug
|
self.irc.debug = cfg.debug
|
||||||
self.irc.run(self.handle, cfg)
|
self.irc.run(self.add_listeners, cfg)
|
||||||
|
|
||||||
def handle(self, msg, channel):
|
def add_listeners(self, cxt):
|
||||||
"""Listen for requests in a channel and match them to functions."""
|
"""Map triggers to handlers."""
|
||||||
self.msg = msg
|
self.irc.listen(cxt, "exit " + cfg.admin_code, self.quit, admin=True)
|
||||||
if self.msg["req_chan"] == cfg.bot_nick:
|
self.irc.listen(cxt, "rollcall", self.rollcall)
|
||||||
# Respond to some commands only from admin user
|
self.irc.listen(cxt, "help", self.rollcall)
|
||||||
if self.msg["user"].lower() == cfg.admin_user.lower() and \
|
self.irc.listen(cxt, "water " + cfg.bot_nick, self.water)
|
||||||
cfg.admin_code in self.msg["req"]:
|
self.irc.listen(cxt, "botsnack " + cfg.bot_nick, self.botsnack)
|
||||||
self.handle_admin_req(self.msg["req"], cfg.admin_user)
|
|
||||||
|
|
||||||
# Respond only in the channel the request was made
|
def quit(self, cxt):
|
||||||
if channel == self.msg["req_chan"]:
|
"""Disconnect from the server and quit."""
|
||||||
# General commands
|
self.irc.disconnect("Okay, okay, I'll leave. (´・ω・`)", "noodling off")
|
||||||
if self.msg["req"] == "rollcall" or self.msg["req"] == "help":
|
|
||||||
self.handle_rollcall()
|
|
||||||
elif self.msg["req"] == ("water " + cfg.bot_nick):
|
|
||||||
self.handle_water()
|
|
||||||
elif self.msg["req"] == ("botsnack " + cfg.bot_nick):
|
|
||||||
self.handle_botsnack()
|
|
||||||
|
|
||||||
def handle_admin_req(self, req, admin_user):
|
def rollcall(self, cxt):
|
||||||
"""Perform admin functions."""
|
|
||||||
if "exit" in req:
|
|
||||||
self.irc.send("PRIVMSG", "Okay, okay, I'll leave. (´・ω・`)", \
|
|
||||||
recvr=admin_user)
|
|
||||||
self.irc.send("QUIT", ":noodling off")
|
|
||||||
exit("Shutting down ...")
|
|
||||||
|
|
||||||
def handle_rollcall(self):
|
|
||||||
resp = (
|
resp = (
|
||||||
"一、二、三、らーめん缶! "
|
"一、二、三、らーめん缶! "
|
||||||
"Hello, I am a ramen vending machine. "
|
"Hello, I am a ramen vending machine. "
|
||||||
|
@ -49,21 +33,19 @@ class Ramen:
|
||||||
"!help "
|
"!help "
|
||||||
"Support: +81 012-700-1MIO どうぞめしあがれ。"
|
"Support: +81 012-700-1MIO どうぞめしあがれ。"
|
||||||
)
|
)
|
||||||
self.irc.send("PRIVMSG", resp, recvr=self.msg["req_chan"])
|
self.irc.reply(cxt, resp)
|
||||||
|
|
||||||
def handle_water(self):
|
def water(self, cxt):
|
||||||
resp = [
|
resp = [
|
||||||
("\x01ACTION happily pours the hot liquid into a bowl of noodles "
|
("\x01ACTION happily pours the hot liquid into a bowl of noodles "
|
||||||
"and offers it to ") + self.msg["nick"] + "\x01",
|
"and offers it to ") + cxt["msg"]["nick"] + "\x01",
|
||||||
"( ^_^)o自自o(^_^ )Cheers!",
|
"( ^_^)o自自o(^_^ )Cheers!",
|
||||||
"Water Level [/////////] 200% - Thanks! (^▽^)"
|
"Water Level [/////////] 200% - Thanks! (^▽^)"
|
||||||
]
|
]
|
||||||
self.irc.send("PRIVMSG", resp[randint(0, len(resp)-1)], \
|
self.irc.reply(cxt, resp[randint(0, len(resp)-1)])
|
||||||
recvr=self.msg["req_chan"])
|
|
||||||
|
|
||||||
def handle_botsnack(self):
|
def botsnack(self, cxt):
|
||||||
self.irc.send("PRIVMSG", "Ramen time anytime! ヽ(´▽`)/", \
|
self.irc.reply(cxt, "Ramen time anytime! 自o(´▽`)/")
|
||||||
recvr=self.msg["req_chan"])
|
|
||||||
|
|
||||||
|
|
||||||
app = Ramen()
|
app = Ramen()
|
||||||
|
|
Loading…
Reference in New Issue