]>
jfr.im git - erebus.git/blob - erebus.py
3 # Erebus IRC bot - Author: John Runyon
6 from __future__
import print_function
8 import os
, sys
, select
, MySQLdb
, MySQLdb
.cursors
, time
, random
, gc
9 import bot
, config
, ctlmod
11 class Erebus(object): #singleton to pass around
24 def __init__(self
, nick
, auth
=None):
31 def msg(self
, *args
, **kwargs
):
32 main
.randbot().msg(self
, *args
, **kwargs
)
33 def slowmsg(self
, *args
, **kwargs
):
34 main
.randbot().slowmsg(self
, *args
, **kwargs
)
35 def fastmsg(self
, *args
, **kwargs
):
36 main
.randbot().fastmsg(self
, *args
, **kwargs
)
39 return self
.auth
is not None
41 def authed(self
, auth
):
42 if auth
== '0': self
.auth
= None
43 else: self
.auth
= auth
.lower()
50 c
= main
.query("SELECT level FROM users WHERE auth = %s", (self
.auth
,))
54 self
.glevel
= row
['level']
62 if chan
not in self
.chans
: self
.chans
.append(chan
)
65 self
.chans
.remove(chan
)
67 return len(self
.chans
) == 0
70 def nickchange(self
, newnick
):
73 def __str__(self
): return self
.nick
74 def __repr__(self
): return "<User %r (%d)>" % (self
.nick
, self
.glevel
)
76 class Channel(object):
77 def __init__(self
, name
, bot
):
86 c
= main
.query("SELECT user, level FROM chusers WHERE chan = %s", (self
.name
,))
89 while row
is not None:
90 self
.levels
[row
['user']] = row
['level']
94 def msg(self
, *args
, **kwargs
):
95 self
.bot
.msg(self
, *args
, **kwargs
)
96 def slowmsg(self
, *args
, **kwargs
):
97 self
.bot
.slowmsg(self
, *args
, **kwargs
)
98 def fastmsg(self
, *args
, **kwargs
):
99 self
.bot
.fastmsg(self
, *args
, **kwargs
)
101 def levelof(self
, auth
):
105 if auth
in self
.levels
:
106 return self
.levels
[auth
]
110 def setlevel(self
, auth
, level
, savetodb
=True):
113 c
= main
.query("REPLACE INTO chusers (chan, user, level) VALUES (%s, %s, %s)", (self
.name
, auth
, level
))
115 self
.levels
[auth
] = level
120 def userjoin(self
, user
, level
=None):
121 if user
not in self
.users
: self
.users
.append(user
)
122 if level
== 'op' and user
not in self
.ops
: self
.ops
.append(user
)
123 if level
== 'voice' and user
not in self
.voices
: self
.voices
.append(user
)
124 def userpart(self
, user
):
125 if user
in self
.ops
: self
.ops
.remove(user
)
126 if user
in self
.voices
: self
.voices
.remove(user
)
127 if user
in self
.users
: self
.users
.remove(user
)
129 def userop(self
, user
):
130 if user
in self
.users
and user
not in self
.ops
: self
.ops
.append(user
)
131 def uservoice(self
, user
):
132 if user
in self
.users
and user
not in self
.voices
: self
.voices
.append(user
)
133 def userdeop(self
, user
):
134 if user
in self
.ops
: self
.ops
.remove(user
)
135 def userdevoice(self
, user
):
136 if user
in self
.voices
: self
.voices
.remove(user
)
138 def __str__(self
): return self
.name
139 def __repr__(self
): return "<Channel %r>" % (self
.name
)
141 def __init__(self
, cfg
):
143 self
.trigger
= cfg
.trigger
144 if os
.name
== "posix":
146 self
.po
= select
.poll()
147 else: # f.e. os.name == "nt" (Windows)
148 self
.potype
= "select"
151 def query(self
, *args
, **kwargs
):
152 if 'norecurse' in kwargs
:
153 norecurse
= kwargs
['norecurse']
154 del kwargs
['norecurse']
158 self
.log("[SQL]", "?", "query(%s, %s)" % (', '.join([repr(i
) for i
in args
]), ', '.join([str(key
)+"="+repr(kwargs
[key
]) for key
in kwargs
])))
160 curs
= self
.db
.cursor()
161 res
= curs
.execute(*args
, **kwargs
)
166 except MySQLdb
.MySQLError
as e
:
167 self
.log("[SQL]", "!", "MySQL error! %r" % (e
))
170 return self
.query(*args
, norecurse
=True, **kwargs
)
174 def newbot(self
, nick
, user
, bind
, authname
, authpass
, server
, port
, realname
):
175 if bind
is None: bind
= ''
176 obj
= bot
.Bot(self
, nick
, user
, bind
, authname
, authpass
, server
, port
, realname
)
177 self
.bots
[nick
.lower()] = obj
179 def newfd(self
, obj
, fileno
):
180 self
.fds
[fileno
] = obj
181 if self
.potype
== "poll":
182 self
.po
.register(fileno
, select
.POLLIN
)
183 elif self
.potype
== "select":
184 self
.fdlist
.append(fileno
)
186 def bot(self
, name
): #get Bot() by name (nick)
187 return self
.bots
[name
.lower()]
188 def fd(self
, fileno
): #get Bot() by fd/fileno
189 return self
.fds
[fileno
]
190 def randbot(self
): #get Bot() randomly
191 return self
.bots
[random
.choice(list(self
.bots
.keys()))]
193 def user(self
, _nick
, justjoined
=False, create
=True):
195 if nick
in self
.users
:
196 return self
.users
[nick
]
198 user
= self
.User(_nick
)
199 self
.users
[nick
] = user
202 self
.randbot().conn
.send("WHO %s n%%ant,1" % (nick
))
207 def channel(self
, name
): #get Channel() by name
208 if name
.lower() in self
.chans
:
209 return self
.chans
[name
.lower()]
213 def newchannel(self
, bot
, name
):
214 chan
= self
.Channel(name
.lower(), bot
)
215 self
.chans
[name
.lower()] = chan
219 if self
.potype
== "poll":
220 return [fd
for (fd
, ev
) in self
.po
.poll()]
221 elif self
.potype
== "select":
222 return select
.select(self
.fdlist
, [], [])[0]
224 def connectall(self
):
225 for bot
in self
.bots
.values():
226 if bot
.conn
.state
== 0:
229 def module(self
, name
):
230 return ctlmod
.modules
[name
]
232 def log(self
, source
, level
, message
):
233 print("%09.3f %s [%s] %s" % (time
.time() % 100000, source
, level
, message
))
235 def getuserbyauth(self
, auth
):
236 return [u
for u
in self
.users
.values() if u
.auth
== auth
.lower()]
239 def hook(self
, word
, handler
):
241 self
.msghandlers
[word
].append(handler
)
243 self
.msghandlers
[word
] = [handler
]
244 def unhook(self
, word
, handler
):
245 if word
in self
.msghandlers
and handler
in self
.msghandlers
[word
]:
246 self
.msghandlers
[word
].remove(handler
)
247 def hashook(self
, word
):
248 return word
in self
.msghandlers
and len(self
.msghandlers
[word
]) != 0
249 def gethook(self
, word
):
250 return self
.msghandlers
[word
]
252 def hooknum(self
, word
, handler
):
254 self
.numhandlers
[word
].append(handler
)
256 self
.numhandlers
[word
] = [handler
]
257 def unhooknum(self
, word
, handler
):
258 if word
in self
.numhandlers
and handler
in self
.numhandlers
[word
]:
259 self
.numhandlers
[word
].remove(handler
)
260 def hasnumhook(self
, word
):
261 return word
in self
.numhandlers
and len(self
.numhandlers
[word
]) != 0
262 def getnumhook(self
, word
):
263 return self
.numhandlers
[word
]
265 def hookchan(self
, chan
, handler
):
267 self
.chanhandlers
[chan
].append(handler
)
269 self
.chanhandlers
[chan
] = [handler
]
270 def unhookchan(self
, chan
, handler
):
271 if chan
in self
.chanhandlers
and handler
in self
.chanhandlers
[chan
]:
272 self
.chanhandlers
[chan
].remove(handler
)
273 def haschanhook(self
, chan
):
274 return chan
in self
.chanhandlers
and len(self
.chanhandlers
[chan
]) != 0
275 def getchanhook(self
, chan
):
276 return self
.chanhandlers
[chan
]
281 main
.db
= MySQLdb
.connect(host
=cfg
.dbhost
, user
=cfg
.dbuser
, passwd
=cfg
.dbpass
, db
=cfg
.dbname
, cursorclass
=MySQLdb
.cursors
.DictCursor
)
286 cfg
= config
.Config('bot.config')
288 if cfg
.getboolean('debug', 'gc'):
289 gc
.set_debug(gc
.DEBUG_LEAK
)
291 pidfile
= open(cfg
.pidfile
, 'w')
292 pidfile
.write(str(os
.getpid()))
297 autoloads
= [mod
for mod
, yes
in cfg
.items('autoloads') if int(yes
) == 1]
298 for mod
in autoloads
:
299 ctlmod
.load(main
, mod
)
302 c
= main
.query("SELECT nick, user, bind, authname, authpass FROM bots WHERE active = 1")
307 main
.newbot(row
['nick'], row
['user'], row
['bind'], row
['authname'], row
['authpass'], cfg
.host
, cfg
.port
, cfg
.realname
)
311 poready
= main
.poll()
312 for fileno
in poready
:
313 for line
in main
.fd(fileno
).getdata():
314 main
.fd(fileno
).parse(line
)
316 if __name__
== '__main__':
317 try: os
.rename('logfile', 'oldlogs/%s' % (time
.time()))
319 sys
.stdout
= open('logfile', 'w', 1)
320 sys
.stderr
= sys
.stdout