2
# Copyright (c) 2002-2004, Jeremiah Fincher
5
# Redistribution and use in source and binary forms, with or without
6
# modification, are permitted provided that the following conditions are met:
8
# * Redistributions of source code must retain the above copyright notice,
9
# this list of conditions, and the following disclaimer.
10
# * Redistributions in binary form must reproduce the above copyright notice,
11
# this list of conditions, and the following disclaimer in the
12
# documentation and/or other materials provided with the distribution.
13
# * Neither the name of the author of this software nor the name of
14
# contributors to this software may be used to endorse or promote products
15
# derived from this software without specific prior written consent.
17
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
32
from cStringIO import StringIO
34
import supybot.conf as conf
35
import supybot.world as world
36
import supybot.irclib as irclib
37
import supybot.ircmsgs as ircmsgs
38
import supybot.ircutils as ircutils
39
import supybot.registry as registry
40
import supybot.callbacks as callbacks
42
class FakeLog(object):
50
class ChannelLogger(callbacks.Plugin):
52
def __init__(self, irc):
53
self.__parent = super(ChannelLogger, self)
54
self.__parent.__init__(irc)
58
self.flusher = self.flush
59
world.flushers.append(self.flusher)
62
for log in self._logs():
64
world.flushers = [x for x in world.flushers if x is not self.flusher]
66
def __call__(self, irc, msg):
68
# I don't know why I put this in, but it doesn't work, because it
69
# doesn't call doNick or doQuit.
70
# if msg.args and irc.isChannel(msg.args[0]):
71
self.__parent.__call__(irc, msg)
72
if irc in self.lastMsgs:
73
if irc not in self.lastStates:
74
self.lastStates[irc] = irc.state.copy()
75
self.lastStates[irc].addMsg(irc, self.lastMsgs[irc])
77
# We must make sure this always gets updated.
78
self.lastMsgs[irc] = msg
81
for log in self._logs():
85
self.lastStates.clear()
88
for logs in self.logs.itervalues():
89
for log in logs.itervalues():
94
for log in self._logs():
98
if e.args[0] != 'I/O operation on a closed file':
99
self.log.exception('Odd exception:')
101
def logNameTimestamp(self, channel):
102
format = self.registryValue('filenameTimestamp', channel)
103
return time.strftime(format)
105
def getLogName(self, channel):
106
if self.registryValue('rotateLogs', channel):
107
return '%s.log' % (channel)
108
# return '%s.log' % (channel, self.logNameTimestamp(channel))
110
return '%s.log' % channel
112
def getLogDir(self, irc, channel):
113
logDir = conf.supybot.directories.log.dirize(self.name())
114
if self.registryValue('directories'):
115
if self.registryValue('directories.network'):
116
logDir = os.path.join(logDir, irc.network)
117
if self.registryValue('directories.channel'):
118
logDir = os.path.join(logDir, channel)
119
if self.registryValue('directories.timestamp'):
120
format = self.registryValue('directories.timestamp.format')
121
timeDir =time.strftime(format)
122
logDir = os.path.join(logDir, timeDir)
123
if not os.path.exists(logDir):
127
def checkLogNames(self):
128
for (irc, logs) in self.logs.items():
129
for (channel, log) in logs.items():
130
if self.registryValue('rotateLogs', channel):
131
name = self.getLogName(channel)
136
def getLog(self, irc, channel):
139
logs = self.logs[irc]
141
logs = ircutils.IrcDict()
142
self.logs[irc] = logs
147
name = self.getLogName(channel)
148
logDir = self.getLogDir(irc, channel)
149
log = file(os.path.join(logDir, name), 'a')
153
self.log.exception('Error opening log:')
156
def timestamp(self, log):
157
format = conf.supybot.log.timestampFormat()
159
log.write(time.strftime(format))
162
def normalizeChannel(self, irc, channel):
163
return ircutils.toLower(channel)
165
def doLog(self, irc, channel, s, *args):
166
if self.registryValue('enable', channel):
168
channel = self.normalizeChannel(irc, channel)
169
log = self.getLog(irc, channel)
170
if self.registryValue('timestamp', channel):
172
if self.registryValue('stripFormatting', channel):
173
s = ircutils.stripFormatting(s)
175
if self.registryValue('flushImmediately'):
178
def doPrivmsg(self, irc, msg):
179
(recipients, text) = msg.args
180
for channel in recipients.split(','):
181
if irc.isChannel(channel):
182
noLogPrefix = self.registryValue('noLogPrefix', channel)
183
if noLogPrefix and text.startswith(noLogPrefix):
184
text = '-= THIS MESSAGE NOT LOGGED =-'
185
nick = msg.nick or irc.nick
186
if ircmsgs.isAction(msg):
187
self.doLog(irc, channel,
188
'* %s %s\n', nick, ircmsgs.unAction(msg))
190
self.doLog(irc, channel, '<%s> %s\n', nick, text)
192
def doNotice(self, irc, msg):
193
(recipients, text) = msg.args
194
for channel in recipients.split(','):
195
if irc.isChannel(channel):
196
self.doLog(irc, channel, '-%s- %s\n', msg.nick, text)
198
def doNick(self, irc, msg):
200
newNick = msg.args[0]
201
for (channel, c) in irc.state.channels.iteritems():
202
if newNick in c.users:
203
self.doLog(irc, channel,
204
'*** %s is now known as %s\n', oldNick, newNick)
205
def doJoin(self, irc, msg):
206
for channel in msg.args[0].split(','):
207
self.doLog(irc, channel,
208
'*** %s has joined %s\n',
209
msg.nick or msg.prefix, channel)
211
def doKick(self, irc, msg):
212
if len(msg.args) == 3:
213
(channel, target, kickmsg) = msg.args
215
(channel, target) = msg.args
218
self.doLog(irc, channel,
219
'*** %s was kicked by %s (%s)\n',
220
target, msg.nick, kickmsg)
222
self.doLog(irc, channel,
223
'*** %s was kicked by %s\n', target, msg.nick)
225
def doPart(self, irc, msg):
226
for channel in msg.args[0].split(','):
227
self.doLog(irc, channel,
228
'*** %s has left %s\n', msg.nick, channel)
230
def doMode(self, irc, msg):
231
channel = msg.args[0]
232
if irc.isChannel(channel) and msg.args[1:]:
233
self.doLog(irc, channel,
234
'*** %s sets mode: %s %s\n',
235
msg.nick or msg.prefix, msg.args[1],
236
' '.join(msg.args[2:]))
238
def doTopic(self, irc, msg):
239
if len(msg.args) == 1:
240
return # It's an empty TOPIC just to get the current topic.
241
channel = msg.args[0]
242
self.doLog(irc, channel,
243
'*** %s changes topic to "%s"\n', msg.nick, msg.args[1])
245
def doQuit(self, irc, msg):
246
if not isinstance(irc, irclib.Irc):
247
irc = irc.getRealIrc()
248
for (channel, chan) in self.lastStates[irc].channels.iteritems():
249
if msg.nick in chan.users:
250
self.doLog(irc, channel, '*** %s has quit IRC\n', msg.nick)
252
def outFilter(self, irc, msg):
253
# Gotta catch my own messages *somehow* :)
254
# Let's try this little trick...
255
if msg.command in ('PRIVMSG', 'NOTICE'):
256
# Other messages should be sent back to us.
257
m = ircmsgs.IrcMsg(msg=msg, prefix=irc.prefix)
262
Class = ChannelLogger
263
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: