Add listener object
This commit is contained in:
		
							parent
							
								
									3914db4685
								
							
						
					
					
						commit
						45029d9525
					
				
							
								
								
									
										90
									
								
								irc.py
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								irc.py
									
									
									
									
									
								
							| @ -8,18 +8,23 @@ class IRC: | ||||
| 
 | ||||
|     debug = False | ||||
| 
 | ||||
|     def run(self, handler, conf): | ||||
|     def run(self, listen_hook, conf): | ||||
|         """A routine that connects to a server, joins channels, and initialises | ||||
|         the request handler in a loop.""" | ||||
|         self.connect(conf.server, conf.bot_nick) | ||||
|         the request listener in a loop.""" | ||||
|         # 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) | ||||
|         while 1: | ||||
|             sleep(2) | ||||
|             data = self.receive(debug=conf.debug) | ||||
|             self.keep_alive(data) | ||||
|             self.msg = self.parse_msg(data, conf.req_prefix) | ||||
|             data = self.receive() | ||||
|             self.keepalive(data) | ||||
|             self.msg = self.parse(data, conf.req_prefix) | ||||
|             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): | ||||
|         """Connect to the server and sends user/nick information.""" | ||||
| @ -32,11 +37,25 @@ class IRC: | ||||
|             bot_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.""" | ||||
|         self.send("PRIVMSG", resp_msg, recvr=admin_user) | ||||
|         self.send("PRIVMSG", resp_msg, recvr=self.admin_user) | ||||
|         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): | ||||
|         """Send messages given the IRC command and text. Optionally specify a  | ||||
|         message recipient with `recvr=user`.""" | ||||
| @ -54,25 +73,14 @@ class IRC: | ||||
|                     bs.decode("utf-8").strip() + "`") | ||||
|             pass | ||||
| 
 | ||||
|     def receive(self, *args, **kwargs): | ||||
|     def receive(self): | ||||
|         """Get messages from the connected socket.""" | ||||
|         data = self.sock.recv(2040).decode("utf-8").strip("\r\n") | ||||
|         if self.debug: | ||||
|             print("[debug][recv] " + data) | ||||
|         return data | ||||
| 
 | ||||
|     def keep_alive(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 parse_msg(self, line, req_prefix): | ||||
|     def parse(self, line, req_prefix): | ||||
|         """Using received data from a socket, extract the request, the nick and | ||||
|         username of the requester, the channel where the request originated and | ||||
|         return a dictionary of values.""" | ||||
| @ -85,3 +93,41 @@ class IRC: | ||||
|             data["nick"] = line.split("!~", 1)[0][1:] | ||||
|             data["user"] = line.split("!~", 2)[1][0:].split("@", 1)[0] | ||||
|         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 | ||||
| 
 | ||||
| import config as cfg | ||||
| @ -9,39 +8,24 @@ class Ramen: | ||||
|     """Requests with a ramen theme.""" | ||||
| 
 | ||||
|     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.debug = cfg.debug | ||||
|         self.irc.run(self.handle, cfg) | ||||
|         self.irc.run(self.add_listeners, cfg) | ||||
| 
 | ||||
|     def handle(self, msg, channel): | ||||
|         """Listen for requests in a channel and match them to functions.""" | ||||
|         self.msg = msg | ||||
|         if self.msg["req_chan"] == cfg.bot_nick: | ||||
|             # Respond to some commands only from admin user | ||||
|             if self.msg["user"].lower() == cfg.admin_user.lower() and \ | ||||
|                 cfg.admin_code in self.msg["req"]: | ||||
|                 self.handle_admin_req(self.msg["req"], cfg.admin_user) | ||||
|     def add_listeners(self, cxt): | ||||
|         """Map triggers to handlers.""" | ||||
|         self.irc.listen(cxt, "exit " + cfg.admin_code, self.quit, admin=True) | ||||
|         self.irc.listen(cxt, "rollcall", self.rollcall) | ||||
|         self.irc.listen(cxt, "help", self.rollcall) | ||||
|         self.irc.listen(cxt, "water " + cfg.bot_nick, self.water) | ||||
|         self.irc.listen(cxt, "botsnack " + cfg.bot_nick, self.botsnack) | ||||
| 
 | ||||
|         # Respond only in the channel the request was made | ||||
|         if channel == self.msg["req_chan"]: | ||||
|             # General commands | ||||
|             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 quit(self, cxt): | ||||
|         """Disconnect from the server and quit.""" | ||||
|         self.irc.disconnect("Okay, okay, I'll leave. (´・ω・`)", "noodling off") | ||||
| 
 | ||||
|     def handle_admin_req(self, req, admin_user): | ||||
|         """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): | ||||
|     def rollcall(self, cxt): | ||||
|         resp = ( | ||||
|             "一、二、三、らーめん缶! " | ||||
|             "Hello, I am a ramen vending machine. " | ||||
| @ -49,21 +33,19 @@ class Ramen: | ||||
|             "!help " | ||||
|             "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 = [ | ||||
|             ("\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!", | ||||
|             "Water Level [/////////] 200% - Thanks! (^▽^)" | ||||
|             ] | ||||
|         self.irc.send("PRIVMSG", resp[randint(0, len(resp)-1)], \ | ||||
|             recvr=self.msg["req_chan"]) | ||||
|         self.irc.reply(cxt, resp[randint(0, len(resp)-1)]) | ||||
| 
 | ||||
|     def handle_botsnack(self): | ||||
|         self.irc.send("PRIVMSG", "Ramen time anytime! ヽ(´▽`)/", \ | ||||
|             recvr=self.msg["req_chan"]) | ||||
|     def botsnack(self, cxt): | ||||
|         self.irc.reply(cxt, "Ramen time anytime! 自o(´▽`)/") | ||||
| 
 | ||||
| 
 | ||||
| app = Ramen() | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user