-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathiibotng.py
160 lines (142 loc) · 5.15 KB
/
iibotng.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/python
# the new iibot framework
import os
import time
import subprocess
class iibot:
def __init__(self, nick, iidir, iipath="/usr/bin/ii"):
self.nick = nick
self.iidir = iidir
self.iipath = iipath
self.servers = {}
self.commands = {}
self.watchPhrases = {}
self.resources = {}
self.addCommand('exit',self.myQuit)
self.proc = {}
self.halt = 1
def __del__(self):
for resource in self.resources:
print "Purging %s" % ( resource )
self.resources[resource].close()
open(resource,"w").close()
self.disconnect()
##properly quit
def myQuit(me, self, ds, user, msg, key):
self.halt = 1
self.__del__()
## Note, ssl will require the patch at http://tools.suckless.org/ii/patches/ii-1.7-ssl.diff
def connect(self, host, ssl=0):
if host in self.servers:
print "Already connected to %s" % (host)
return -1
cmd = [self.iipath, "-s", host, "-n", self.nick, "-i", self.iidir]
if ssl:
cmd += ["-e","ssl"]
cmd.append("&")
self.proc[host] = subprocess.Popen(cmd)
time.sleep(5)
self.servers[host] = []
self.halt = 0
def disconnect(self, host=None):
if host and host in self.proc:
os.kill(self.proc[host].pid,9)
else:
for host in self.proc:
os.kill(self.proc[host].pid,9)
def joinChannel(self, host, channel):
if host not in self.servers:
return -1
self.writeResource("%s/%s/in" % ( self.iidir, host ), "/j %s" %(channel))
self.servers[host].append(channel)
return 1
## This is where most of the work is done, processing messages that come in
## We pass this the raw message line itself, the "container" which is the messages return dict, and the key
## Key will be the path (minus self.iidir) to a resource dir. This can be used for /in and /out comms
def processMessageLine(self, line, container, key):
if line == "": ## ignore empty lines sent by the IRCD or readlines() having no results
return container
## do message parsing
datestamp = line[0:16]
line = line[16:]
if "> " in line and "<" in line:
user = line.split(">")[0].split("<")[1]
message = line.split("> ")[1]
else:
user = "<IRCD>"
message = line
if user == self.nick: ## ignore yourself
return container
if message[0] == "-" and user != "<IRCD>": ## do commands
command = message.split(" ")[0][1:]
if command in self.commands:
resp = self.commands[command](self, datestamp, user, message, key)
print resp
if user != "<IRCD>": ## respond to watch phrases
for phrase in self.watchPhrases:
if phrase in message:
self.watchPhrases[phrase](self, datestamp, user, message, key)
if key not in container:
container[key] = []
container[key].append((datestamp,user,message))
return container
## a little helper function to make sure resources are the same (as we use them as keys and don't want dups)
## also checks/prepends iidir
def normalizeResource(self, resource):
if self.iidir not in resource:
resource = self.iidir + "/" + resource
resource = resource.replace("//","/")
return resource
## get messages from a location
def readResource(self, resource):
resource = self.normalizeResource(resource)
if resource not in self.resources:
self.resources[resource] = open(resource,"r")
tmp = self.resources[resource].readlines()
data = []
for t in tmp:
data.append(t.replace("\n",""))
return data
## write messages to a resource
def writeResource(self, resource, message):
resource = self.normalizeResource(resource)
if resource not in self.resources:
self.resources[resource] = open(resource, "a")
if message[-1] != "\n":
message = message + "\n"
self.resources[resource].write(message)
self.resources[resource].flush()
## This is used for polling all current messages
## This will return messages as lists under a dict, keyd by resource dirs (e..g irc.url.com/#channame)
## This will also process commands and watch phrases
def getMessages(self):
messages = {}
if self.halt: ## if we're done and have run the exit, we don't want to try and poll file handles we closed (sometimes happens)
return messages
for server in self.servers: ## for each server we're connected to, check for IRCD level messages
out = self.readResource("%s/%s/out" % (self.iidir, server))
for line in out:
messages = self.processMessageLine(line, messages, server)
for channel in self.servers[server]: ## for each channel per server, check for messages
chanout = self.readResource("%s/%s/%s/out" % (self.iidir, server, channel))
for line in chanout:
messages = self.processMessageLine(line, messages, server+"/"+channel)
if len(messages) == 0:
return None
return messages
## bind commands
def addCommand(self, cmd, func):
if cmd in self.commands:
print "Already bound %s to %s" % ( self.commands[cmd], cmd )
return -1
self.commands[cmd] = func
## bind watch phrases
## phrases can be either a string or list of strings that will be "fuzzy" matched against (basically 'if word in line')
def addWatchPhrase(self, phrases, func):
if type(phrases) == type(""):
phrases = [phrases]
for phrase in phrases:
if phrase in self.watchPhrases:
print "Already using '%s' for %s" % ( phrase, func)
return -1
self.watchPhrases[phrase] = func