-
Notifications
You must be signed in to change notification settings - Fork 5
/
commandHandler.py
317 lines (252 loc) · 13.8 KB
/
commandHandler.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
import imp, os
import Queue
import logging
from time import strftime
from timeit import default_timer
from datetime import datetime
import centralizedThreading
from BotEvents import TimerEvent, MsgEvent, StandardEvent
from IRC_registration import trackVerification
from CommandHelp import HelpModule
from IRCLogging import LoggingModule
from BanList import BanList
class commandHandling():
def __init__(self, channels, cmdprefix, name, ident, adminlist, loglevel):
self.LoggingModule = LoggingModule(loglevel)
self.__CMDHandler_log__ = logging.getLogger("CMDHandler")
self.name = name
self.ident = ident
self.Plugin = self.__LoadModules__("IRCpackets")
self.commands = self.__LoadModules__("commands")
self.bot_userlist = adminlist
self.Bot_Auth = trackVerification(adminlist)
self.channels = channels
self.channelData = {}
self.topic = {}
self.cmdprefix = cmdprefix
self.events = {"time" : TimerEvent(), "chat" : MsgEvent(),
"channeljoin" : StandardEvent(),
"channelpart" : StandardEvent(),
"channelkick" : StandardEvent(),
"userquit" : StandardEvent(),
"nickchange" : StandardEvent()}
self.events["time"].addEvent("LogfileSwitch", 60, self.LoggingModule.__switch_filehandle_daily__)
self.server = None
self.latency = None
self.rankconvert = {"@@" : 3, "@" : 2, "+" : 1, "" : 0}
self.startupTime = datetime.now()
self.PacketsReceivedBeforeDeath = Queue.Queue(maxsize = 50)
self.threading = centralizedThreading.ThreadPool()
self.Banlist = BanList("BannedUsers.db")
self.helper = HelpModule()
self.auth = None
def handle(self, send, prefix, command, params, auth):
self.send = send
## In the next few lines I implement a basic logger so the logs can be put out when the bot dies.
## Should come in handy when looking at what or who caused trouble
## There is room for 50 entries, number can be increased or lowered at a later point
try:
self.PacketsReceivedBeforeDeath.put(u"{0} {1} {2}".format(prefix, command, params), False)
except Queue.Full:
self.PacketsReceivedBeforeDeath.get(block = False)
self.PacketsReceivedBeforeDeath.put(u"{0} {1} {2}".format(prefix, command, params), False)
try:
if command in self.Plugin:
self.Plugin[command][0].execute(self, send, prefix, command, params)
else:
# 0 is the lowest possible log level. Messages about unimplemented packets are
# very common, so they will clutter up the file even if logging is set to DEBUG
self.__CMDHandler_log__.log(0, "Unimplemented Packet: %s", command)
except KeyError as error:
#print "Unknown command '"+command+"'"
self.__CMDHandler_log__.exception("Missing channel or other KeyError caught")
print "Missing channel or other KeyError caught: "+str(error)
def timeEventChecker(self):
self.events["time"].tryAllEvents(self)
def userGetRank(self, channel, username):
#print self.channelData[channel]["Userlist"]
for user in self.channelData[channel]["Userlist"]:
if user[0].lower() == username.lower():
return user[1]
def userGetRankNum(self, channel, username):
if username in self.bot_userlist and self.Bot_Auth.isRegistered(username):
return 3
else:
for user in self.channelData[channel]["Userlist"]:
if user[0].lower() == username.lower():
if user[1] == "@@":
return 2
else:
return self.rankconvert[user[1]]
return -1 # No user found
def retrieveTrueCase(self, channel):
for chan in self.channelData:
if chan.lower() == channel.lower():
return chan
return False
# A wrapper for sendChatMessage that does not require a send argument.
def sendMessage(self, channel, msg, msgsplitter = None, splitAt = " "):
self.sendChatMessage(self.send, channel, msg, msgsplitter, splitAt)
def sendChatMessage(self, send, channel, msg, msgsplitter = None, splitAt = " "):
# we calculate a max length value based on what the server would send to other users
# if this bot sent a message.
# Private messages from the server look like this:
# nick!user@hostname PRIVMSG target :Hello World!
# Nick is the username of the bot, user is the identification name of the bot and can be
# different from the nick, it will prefix the hostname. target is the channel
# to which we send the message. At the end, we add a constant (25) to the length to account
# for whitespaces and other characters and eventual oddities.
# The Hostname will be limited to 63, regardless of the actual length.
# 7 characters for the PRIVSM string
# if you want to create your own tweaked message splitter,
# provide it as the fourth argument to self.sendChatMessage
# otherwise, the default one, i.e. self.defaultsplitter, is used
if msgsplitter == None:
msgsplitter = self.defaultsplitter
prefixLen = len(self.name) + len(self.ident) + 63 + 7 + len(channel) + 25
remaining = 512-prefixLen
#print remaining
if len(msg)+prefixLen > 512:
msgpart = msgsplitter(msg, remaining, splitAt)
self.__CMDHandler_log__.debug("Breaking message %s into parts %s", msg, msgpart)
for part in msgpart:
#send("PRIVMSG {0} :{1}".format(channel, part))
#send("PRIVMSG "+str(channel)+" :"+str(part))
send(u"PRIVMSG {0} :{1}".format(channel, part))
self.__CMDHandler_log__.debug("Sending parted message to channel/user %s: '%s'", channel, msg)
else:
#send("PRIVMSG {0} :{1}".format(channel, msg))
#send("PRIVMSG "+channel+" :"+msg)
send(u"PRIVMSG {0} :{1}".format(channel, msg))
self.__CMDHandler_log__.debug("Sending to channel/user %s: '%s'", channel, msg)
def sendNotice(self, destination, msg, msgsplitter = None, splitAt = " "):
# Works the same as sendChatMessage
# Only difference is that this message is sent as a NOTICE,
# and it does not require a send parameter.
if msgsplitter == None:
msgsplitter = self.defaultsplitter
#NOTICE
prefixLen = len(self.name) + len(self.ident) + 63 + 6 + len(destination) + 25
remaining = 512-prefixLen
#print remaining
if len(msg)+prefixLen > 512:
msgpart = msgsplitter(msg, remaining, splitAt)
self.__CMDHandler_log__.debug("Breaking message %s into parts %s", msg, msgpart)
for part in msgpart:
#self.send("NOTICE "+str(destination)+" :"+str(part))
self.send(u"NOTICE {0} :{1}".format(destination, part))
self.__CMDHandler_log__.debug("Sending parted notice to channel/user %s: '%s'", destination, msg)
else:
#self.send("NOTICE "+str(destination)+" :"+str(msg))
self.send(u"NOTICE {0} :{1}".format(destination, msg))
self.__CMDHandler_log__.debug("Sending notice to channel/user %s: '%s'", destination, msg)
def defaultsplitter(self, msg, length, splitAt):
start = 0
end = length
items = []
while end <= len(msg):
splitpos = msg[start:end].rfind(splitAt)
# case 1: whitespace has not been found, ergo:
# message is too long, so we split it at the position specified by 'end'
if splitpos < 0:
items.append(msg[start:end])
start = end
# case 2: whitespace has been found, ergo:
# we split it at the whitespace
# splitpos is a value local to msg[start:end], so we need to add start to it to get a global value
else:
items.append(msg[start:start+splitpos])
start = start+splitpos+len(splitAt)
end = start + length
# Check if there is any remaining data
# If so, append the remaining data to the list
if start < len(msg):
items.append(msg[start:])
# remove all empty strings in the list because they are not needed nor desired
for i in range(items.count("")):
items.remove("")
return items
## writeQueue adds a specified string to the internal queue of the bot.
## This functions handles marking the string with a DebugEntry prefix and the time
## at which the entry was added. You can also specify a name that will be added to
## the entry so that you can identify which module or command has created the entry.
##
##
## Please note that at the time of this writing the queue can hold a maximum of 50 entries.
## Adding new entries will kick the oldest entries out of the queue, so you should be
## conservative with the usage of writeQueue.
# UPDATE: writeQueue is now deprecated, please use Python's logging module.
# The logging module allows you to have seperate info and debug messages which will
# be written automatically into log files. These are not limited to an arbitrary
# number and will (should) not disappear on repeated crashes. Read up on how to use the logging module.
# writeQueue messages will be written to the log files for the sake of improved compatibility
def writeQueue(self, string, modulename = "no_name_given"):
entryString = "DebugEntry at {0} [{1!r}]: {2!r}".format(strftime("%H:%M:%S (%z)"), modulename, string)
self.__CMDHandler_log__.debug("Added DebugEntry: '%s'", entryString)
try:
self.PacketsReceivedBeforeDeath.put(entryString, False)
except Queue.Full:
self.PacketsReceivedBeforeDeath.get(block = False)
self.PacketsReceivedBeforeDeath.put(entryString, False)
def joinChannel(self, send, channel):
if isinstance(channel, str):
if channel not in self.channelData:
#self.channels.append(channel)
self.channelData[channel] = {"Userlist" : [], "Topic" : "", "Mode" : ""}
send("JOIN "+channel, 5)
self.__CMDHandler_log__.info("Joining channel: '%s'", channel)
elif isinstance(channel, list):
for chan in channel:
if chan not in self.channelData:
#self.channels.append(channel)
self.channelData[chan] = {"Userlist" : [], "Topic" : "", "Mode" : ""}
send("JOIN "+",".join(channel), 3)
self.__CMDHandler_log__.info("Joining several channels: '%s'", channel)
else:
self.__CMDHandler_log__.error("Trying to join a channel, but channel is not list or string: %s [%s]", channel, type(channel))
raise TypeError
print self.channelData
def whoisUser(self, user):
self.send("WHOIS {0}".format(user))
self.Bot_Auth.queueUser(user)
self.__CMDHandler_log__.debug("Sending WHOIS for user '%s'", user)
def userInSight(self, user):
print self.channelData
self.__CMDHandler_log__.debug("Checking if user '%s' is in the following channels: %s", user, self.channelData.keys())
for channel in self.channelData:
for userD in self.channelData[channel]["Userlist"]:
if user == userD[0]:
return True
self.__CMDHandler_log__.debug("Yes, he is (at least) in channel '%s'", channel)
return False
self.__CMDHandler_log__.debug("No, user is out of sight.")
def __ListDir__(self, dir):
files = os.listdir(dir)
newlist = []
self.__CMDHandler_log__.debug("Listing files in directory '%s'", dir)
for i in files:
if not i.startswith("__init__") and i.endswith(".py"):
newlist.append(i)
return newlist
def __LoadModules__(self,path):
ModuleList = self.__ListDir__(path)
self.__CMDHandler_log__.info("Loading modules in path '%s'...", path)
Packet = {}
for i in ModuleList:
self.__CMDHandler_log__.debug("Loading file %s in path '%s'", i, path)
module = imp.load_source("RenolIRC_"+i[0:-3], path+"/"+i)
#print i
Packet[module.ID] = (module, path+"/"+i)
try:
if not callable(module.__initialize__):
module.__initialize__ = False
self.__CMDHandler_log__.log(0, "File %s does not use an initialize function", i)
except AttributeError:
module.__initialize__ = False
self.__CMDHandler_log__.log(0, "File %s does not use an initialize function", i)
Packet[module.ID] = (module, path+"/"+i)
#Packet[i[1].lower()].PATH = path + "/"+i[2]
#self.Packet[i[1]] = self.Packet[i[1]].EXEC()
print "ALL MODULES LOADED"
self.__CMDHandler_log__.info("Modules in path '%s' loaded.", path)
return Packet