123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- #! /usr/bin/python3
- import json
- import threading
- import queue
- import time
- import os
- import pdb
- import re
- import select
- import signal
- import socket
- import sys
- import traceback
- import logging, logging.handlers
- import myLogger
- import unixsocket
- def signal_handler(signum,frame):
- if signum == signal.SIGUSR1:
- pdb.set_trace()
- signal.signal(signal.SIGUSR1,signal_handler)
- PDB = False
- # PDB = True
- # from .irc_numeric_name import *
- # https://tools.ietf.org/html/rfc2812#section-2.3.1
- p_chanstring = r"[^\x00\x07\r\n ,:]"
- p_channelid = r"[a-z0-9]{5}"
- p_user = r"[^\x00\r\n @]+"
- p_key = r"[\x01-05\x07-08\x0C\x0E-F1\x21-7F]{1,23}"
- p_letter = r"[a-zA-Z0-9]"
- p_digit = r"[0-9]"
- p_hexdigit = r"("+p_digit+r")|[A-F]"
- # p_special = r"[\x5B\x60]"#\x7B-7D]"
- # p_special = r"[\[\],{}`\\_^{|}]"
- p_special = r"[]{}|,`_^\\]|\["
- p_nospcrlfcl = r"[^\x00\x07\r\n :]"
- # p_nospcrlfcl = r"[^\x00\x07\r\n]"
- p_wildone = r"\x3F"
- p_wildmany = r"\x2A"
- p_nowild = r"[\x01-29\x2B-3E\x40-FF]"
- p_noesc = r"[\x01-5B\x5D-FF]"
- p_nowild = r"[^\x3F\x2A]"
- p_noesc = r"[^\x00\\]"
- p_mask = r"(("+p_nowild+r")|("+p_noesc+p_wildone+r")|("+p_noesc+p_wildmany+r"))*"
- p_targetmask = r"\$|#"+p_mask
- p_channel = r"(#|\+|(!"+p_channelid+r")|&)("+p_chanstring+r")+(:("+p_chanstring+r")+)?"
- # p_nickname = r"("+p_letter+r")|("+p_special+r")(("+p_letter+r")|("+p_digit+r")|("+p_special+r")){0,8}"
- # p_nickname = r"(("+p_letter+r")|("+p_special+r"))(("+p_letter+r")|("+p_digit+r")|("+p_special+r")){1,8}"
- p_nickname = r"(("+p_letter+r")|("+p_special+r"))(("+p_letter+r")|("+p_digit+r")|("+p_special+r"))+"
- p_shortname = r"(("+p_letter+r")|("+p_digit+r"))"\
- + r"(("+p_letter+r")|("+p_digit+r")|(-))*"\
- + r"(("+p_letter+r")|("+p_digit+r"))*"
- p_shortname = r"("+p_letter+r"|"+p_digit+r")"\
- + r"("+p_letter+r"|"+p_digit+r"|-)*"\
- + r"("+p_letter+r"|"+p_digit+r")*"
- p_shortname = r"("+p_letter+r"|"+p_digit+r")"\
- + r"(("+p_letter+r")|("+p_digit+r")|(-))*"\
- + r"(("+p_letter+r")|("+p_digit+r"))*"
- p_hostname = r"("+p_shortname+r")(\."+p_shortname+r")*" # dot
- p_servername = p_hostname
- p_ip4addr = r"("+p_digit+r"){1,3}\."+r"("+p_digit+r"){1,3}\."+r"("+p_digit+r"){1,3}\."+r"("+p_digit+r"){1,3}"
- p_ip6addr = r"(("+p_hexdigit+r")+(:("+p_hexdigit+r")+){7})"\
- + r"|(0:0:0:0:0:(0|(FFFF)):"+p_ip4addr+r")"
- p_hostaddr = r"("+p_ip4addr+r")|("+p_ip6addr+r")"
- p_host = r"("+p_hostname+r")|("+p_hostaddr+r")"
- p_middle = p_nospcrlfcl+r"((:)|("+p_nospcrlfcl+r"))*"
- p_trailing = r"((:)|( )|("+p_nospcrlfcl+r"))*"
- # p_trailing = r"((:)|( )|("+p_nospcrlfcl+r"))+"
- p_params = r"(( "+p_middle+r"){0,13}( :"+p_trailing+r"))|(( "+p_middle+r"){14}( :?"+p_trailing+r"))"
- # p_params = r"(( "+p_middle+r"){0,13}( :"+p_trailing+r")?)|(( "+p_middle+r"){14}( :?"+p_trailing+r")?)"
- p_command = r"("+p_letter+r")+|(\d){3}"
- p_prefix_client = p_nickname+r"((!"+p_user+r")?@"+p_host+r")?"
- p_prefix = r"("+p_servername+r")|("+p_prefix_client+r")"
- p_message = r"(:"+p_prefix+r" )?"+p_command+r"("+p_params+r")?"
- p_target = r"("+p_nickname+r")|("+p_servername+r")" #p_server
- p_msgto = r"("+p_channel+r")"\
- +r"|("+p_user+r"(%"+p_host+r")?@"+p_servername+r")"\
- +r"|("+p_user+r"%"+p_host+r")"\
- +r"|("+p_targetmask+r")"\
- +r"|("+p_nickname+r")"\
- +r"|("+p_nickname+r"!"+p_user+r"@"+p_host+r")"
- p_msgtarget = p_msgto+r"(,"+p_msgto+r")*"
- # "server":re.compile(
- # r":(?P<servername>"+p_servername+r")"+
- # r" "+
- # r"(?P<command>"+p_command+r")"+
- # r"(?P<params>"+p_params+r")"
- # ),
- # "client":re.compile(
- # r":(?P<identity>"+
- # r"(?P<nick>"+p_nickname+r")"+
- # r"!(?P<user>"+p_user+r")"+
- # r"@(?P<host>"+p_host+r")"+
- # r")"+
- # r" "+
- # r"(?P<command>"+p_command+r")"+
- # # r"(\s?:?)"+
- # r"(?P<params>"+p_params+r")"
- # ),
- class IRCClient(unixsocket.Client):
- def __init__(self, *args, **kwargs):
- self.super_object = super(IRCClient, self)
- self.super_object.__init__(*args, block_size = 2048, **kwargs)
- # all item in this dict are searched for in every message
- # until the item is found the value defaults to None. when found, the message is the value
- self.search_items = {}
- self.pattern = {
- "full_message":re.compile(
- r"(:"+
- r"(?P<identity>"+
- r"(?P<nick>"+p_nickname+r")"+
- r"("+
- r"(!(?P<user>"+p_user+r"))?"+
- r"@(?P<host>"+p_host+r") ?"+
- r")?"+
- r")"+
- r"|(?P<servername>"+p_servername+r")"+
- r" )?"+
- r"(?P<command>"+p_command+r")"+
- r"(?P<params>"+p_params+r")?"
- ),
- "chat":re.compile(
- # r" ("+p_nickname+r")|("+p_channel+r") :("+p_nickname+r": )?("+p_trailing+r")"
- r"(?P<target>("+p_channel+r")|("+p_nickname+r")) :"+
- r"("+
- r"(?P<highlight>"+p_nickname+r"): ?"+
- r"(?P<msg_command>[:,][a-zA-Z0-9_-]+)? ?"
- r")?"+
- r"(?P<rest>.*)"
- ),
- }
- # this dict holds client information
- self.client_info = {
- "user":None,
- "hostname":None, #!!!
- "servername":None, #!!!
- "realname":"irc bot",
- "identity":None,
- "pass_auth":False,
- "nickserv_auth":False,
- "current_nick":None,
- "last_nick":None,
- "server_name":None, #!!
- "connection_address":None,
- }
- self.client_info.update({
- "user":"baum",
- "hostname":"digital",
- "servername":"omnimaga",
- "realname":"irc bot",
- "pass_auth":False,
- "nickserv_auth":False,
- "startup_chans":["#thebot", "#digital"],
- "chan_hello":"heyho, I'm a bot",
- "server_name":self.host,
- "connection_address":self.host
- })
- self.config = {
- "shown_message_types":["numeric","private","client","PING","unknown"],
- "nicks":["kevin","clemens","kevin_","kevin__"],
- }
- self.__private_config = {
- "pass_password":"pass_pwd",
- "nickserv_password":"nickserv_pwd"
- }
- self.server_info = {
- "names":{#"#chan":[".user1",".user2"]
- },
- }
- ## Events
- self.registered_event = threading.Event()
- def handshake(self):
- nickname = self.config["nicks"][0]
- nick_not_accepted_msg = " 433 * "+nickname+" :Nickname is already in use."
- # welcome message, no MOTD, MOTD end, nickname already taken
- self.add_search_item(" 001 "," 422 "," 376 ", nick_not_accepted_msg)
- time.sleep(0.5)
- if self.client_info["pass_auth"]:
- auth_pass(self)
- self.send("NICK "+nickname)
- self.send("USER {identity} {hostname} {servername} {realname}\r\n".format(**self.client_info))
- self.add_search_item(nick_not_accepted_msg," 001 ")
- nicks = iter(self.config["nicks"][1:])
- while True:
- if self.search_items[nick_not_accepted_msg]:
- del self.search_items[nick_not_accepted_msg]
- try:
- nickname = next(nicks)
- except StopIteration:
- self.stop()
- nick_not_accepted_msg = " 433 * "+nickname+" :Nickname is already in use."
- self.search_items[nick_not_accepted_msg] = None
- self.send("NICK "+nickname)
- elif self.search_items[" 001 "]:
- self.client_info["current_nick"] = nickname
- self.client_info["server_name"] = self.search_items[" 001 "].split()[0].lstrip(":")
- break
- self.logger.debug(self.client_info["server_name"])
- self.registered_event.set()
- # self.wait_for_item(" 422 "," 376 ")
- if self.client_info["nickserv_auth"]:
- auth_nickserv(self)
- self.join_chans(*self.client_info["startup_chans"],message=self.client_info["chan_hello"])
- time.sleep(0.1)
- self.send("MODE kevin +R")
- def connect(self):
- handshake_thread = threading.Thread(target=self.handshake, name="handshake", daemon=True)
- # calling connect function of the superclass
- self.super_object.connect()
- # start handshake thread
- handshake_thread.start()
- def handle_message(self, message, message_data, *args, **kwargs):
- pass
- def handle_data(self, data):
- for message in data.split("\r\n"):
- # skip the message if it is empty
- message = message.strip()
- if not message:
- continue
- # search for every item in self.search_items
- self.search_in(message)
- # extract information
- message_data = self.extract_message(message)
- # log message
- if message_data["known"]:
- self.logger.info(message)
- print(json.dumps(message_data,indent=4))
- else:
- self.logger.info(message)
- print("\033[31;1m"+json.dumps(message_data,indent=4)+"\033[0m")
- # answer PINGs
- if message.find("PING ") == 0:
- self.send("PONG :"+"".join(message.split(":")[1:]))
- self.handle_message(message,message_data)
- # if self.registered_event.is_set():
- # pass
- def extract_message(self, message):
- message_data = {}
- # message_data["type"]="\033[31;1m"+"unknown"+"\033[0m"
- try:
- full_msg_match = self.pattern["full_message"].search(message)
- message_data["full_msg_match"] = str(full_msg_match)
- if not full_msg_match:
- message_data["known"]=False
- return message_data
- message_data.update(full_msg_match.groupdict())
- message_data["known"] = True
- # message_data["split"] = message.split()
- message_data["params"] = message_data["params"].lstrip()
- # command specific information
- # if message_data["command"] in ["PRIVMSG", "NOTICE"]:
- # chat_match=self.pattern["chat"].search(message_data["params"])
- # message_data.update(chat_match.groupdict())
- # message_data["private_msg"] = message_data["target"] == self.client_info["current_nick"]
- # elif message_data["command"] in ["PART","QUIT"]:
- # message_data["target"],message_data["msg"] = message_data["split"]
- # chat_match=self.pattern["chat"].search(message_data["params"])
- # self.logger.info(chat_match.groups())
- # message_data.update(chat_match.groupdict())
- except Exception as e:
- self.logger.error(message)
- self.logger.error(e, exc_info=True)
- finally:
- return message_data
- # search functions:
- def search_in(self, text):
- for item in self.search_items:
- if text.find(item) != -1:
- self.search_items[item] = text
- def add_search_item(self, *args):
- for item in args:
- self.search_items[item] = self.search_items.get(item, None)
- def remove_search_item(self, *args):
- for item in args:
- del self.search_items[item]
- def wait_for_item(self, *args,timeout=0):
- args = list(args)
- new_items = set(args) - set(self.search_items)
- self.add_search_item(*new_items)
- end_time = time.time() + timeout
- while not self.exit_event.is_set():
- for item in args:
- if self.search_items[item] != None:
- self.logger.debug("item found: " + self.search_items[item])
- return item
- time.sleep(0.1)
- if timeout:
- if time.time() >= end_time:
- return False
- # irc functions
- def change_nick(self, nickname):
- self.send("NICK "+nickname)
- # self.client_info["last_nick"] = self.client_info["current_nick"]
- # self.client_info["current_nick"] = nickname
- def join_chans(self, *chans, message=None):
- for c in chans:
- self.send("JOIN " + c)
- if message:
- self.chat(c, message)
- def chat(self, target, message):
- self.send("PRIVMSG {target} {message}".format(target=target,message=message))
- class PluginHandler(object):
- """docstring for IRCPluginHandler"""
- def __init__(self, client_object, config_str=None, config_file=None):
- self.super_object = super(PluginHandler,self)
- self.super_object.__init__(client_object)
- self.client_object = client_object
- self.plugin_objects = {}
- self.config_dict = {}
- def handle_message(self,message,message_data):
- print(message)
- class PluginBase(object):
- def __init__(self):
- self.super_object = super(PluginBase, self)
- self.super_object.__init()
- def handle():
- pass
- class IRCPluginHandler(PluginHandler):
- def __init__(self,irc_client):
- self.super_object = super(IRCPluginHandler,self)
- self.super_object.__init__(irc_client)
- self.irc_client = irc_client
|