清理 app.yaml 及对应的文件
[gaetalk.git] / lilytalk.py
blob8bf77da24456d7a993aa5d5009d771e6df5a50f4
1 #!/usr/bin/env python2
2 # vim:fileencoding=utf-8
4 import re
5 import logging
6 import datetime
8 from google.appengine.ext import db
9 from google.appengine.api import xmpp
11 import utils
12 import config
14 notice = u'这是一个测试群'
15 helpre = re.compile(r'^\W{0,2}help$')
17 #用户所有资源离线时,会加上“完全”二字
18 OFFLINE = u'离线'
19 AWAY = u'离开'
20 XAWAY = u'离开'
21 BUSY = u'忙碌'
22 ONLINE = u'在线'
23 CHAT = u'和我说话吧'
25 NEW = u'加入'
26 LEAVE = u'退出'
27 NICK = u'昵称更改 (%s -> %s)'
28 SNOOZE = u'snooze %ds'
29 BLACK = u'禁言 %s %ds'
30 BLACK_AUTO = u'被禁言 %ds'
31 KICK = u'删除 %s (%s)'
32 ADMIN = u'%s 成为管理员 (by %s)'
33 UNADMIN = u'%s 不再是管理员 (by %s)'
35 STATUS_CODE = {
36 '': ONLINE,
37 'away': AWAY,
38 'dnd': BUSY,
39 'xa': XAWAY,
40 'chat': CHAT,
43 #状态的排序顺序
44 STATUS_LIST = [CHAT, ONLINE, AWAY, XAWAY, BUSY, OFFLINE]
46 timezone = datetime.timedelta(hours=config.timezoneoffset)
48 class User(db.Model):
49 jid = db.StringProperty(required=True, indexed=True)
50 nick = db.StringProperty(required=True, indexed=True)
52 add_date = db.DateTimeProperty(auto_now_add=True)
53 last_online_date = db.DateTimeProperty()
54 last_offline_date = db.DateTimeProperty()
55 last_speak_date = db.DateTimeProperty()
57 msg_count = db.IntegerProperty(required=True, default=0)
58 msg_chars = db.IntegerProperty(required=True, default=0)
59 credit = db.IntegerProperty(required=True, default=0)
61 black_before = db.DateTimeProperty(auto_now_add=True)
62 snooze_before = db.DateTimeProperty()
63 flooding_point = db.IntegerProperty(default=0)
65 avail = db.StringProperty(required=True)
66 is_admin = db.BooleanProperty(required=True, default=False)
67 resources = db.StringListProperty(required=True)
68 #允许他人私信?
69 reject_pm = db.BooleanProperty(default=False)
71 prefix = db.StringProperty(required=True, default=config.default_prefix)
72 nick_pattern = db.StringProperty(required=True, default='[%s]')
73 nick_changed = db.BooleanProperty(required=True, default=False)
74 intro = db.StringProperty()
76 class Log(db.Model):
77 time = db.DateTimeProperty(auto_now_add=True, indexed=True)
78 msg = db.StringProperty(required=True, multiline=True)
79 jid = db.StringProperty()
80 nick = db.StringProperty()
81 type = db.StringProperty(required=True, indexed=True,
82 choices=set(['chat', 'member', 'misc']))
84 def log_msg(sender, msg):
85 l = Log(jid=sender.jid, nick=sender.nick,
86 type='chat', msg=msg)
87 l.put()
89 def log_onoff(sender, action, resource=''):
90 if resource:
91 if action == OFFLINE and not sender.resources:
92 msg = u'完全%s (%s)' % (action, resource)
93 else:
94 msg = '%s (%s)' % (action, resource)
95 else:
96 msg = action
97 l = Log(jid=sender.jid, nick=sender.nick,
98 type='member', msg=msg)
99 l.put()
101 def get_user_by_jid(jid):
102 return User.gql('where jid = :1', jid.lower()).get()
104 def get_user_by_nick(nick):
105 return User.gql('where nick = :1', nick).get()
107 def get_member_list():
108 r = []
109 now = datetime.datetime.now()
110 #一个查询中最多只能有一个不等比较
111 l = User.gql('where avail != :1', OFFLINE)
112 for u in l:
113 r.append(u)
114 return [unicode(x.jid) for x in r \
115 if x.snooze_before is None or x.snooze_before < now]
117 def send_to_all_except(jid, message):
118 if isinstance(jid, str):
119 jids = [x for x in get_member_list() if x != jid]
120 else:
121 jids = [x for x in get_member_list() if x not in jid]
122 logging.debug(jid)
123 logging.debug(jids)
124 try:
125 xmpp.send_message(jids, message)
126 except xmpp.InvalidJidError:
127 pass
129 def send_to_all(message):
130 jids = get_member_list()
131 xmpp.send_message(jids, message)
133 def handle_message(msg):
134 sender = get_user_by_jid(msg.sender.split('/')[0])
135 if sender is None:
136 msg.reply('很抱歉,出错了,请重新添加好友。')
137 return
138 if msg.body.startswith('?OTR:'):
139 msg.reply('不支持 OTR 加密!')
140 return
141 if len(msg.body) > 500:
142 msg.reply('由于技术限制,每条消息最长为 500 字。大段文本请贴 paste 网站。')
143 return
144 if sender.is_admin or sender.jid == config.root:
145 ch = AdminCommand(msg, sender)
146 else:
147 ch = BasicCommand(msg, sender)
148 if not ch.handled:
149 now = datetime.datetime.now()
150 if sender.black_before is not None \
151 and sender.black_before > now:
152 if (datetime.datetime.today()+timezone).date() == \
153 (sender.black_before+timezone).date():
154 format = '%H时%M分%S秒'
155 else:
156 format = '%m月%d日 %H时%M分%S秒'
157 msg.reply('你已被禁言至 ' \
158 + (sender.black_before+timezone).strftime(format))
159 return
161 if sender.last_speak_date is not None:
162 d = now - sender.last_speak_date
163 t = d.seconds
164 if d.days > 0 or t > 60:
165 sender.flooding_point = 0
166 else:
167 k = 1000 / (t * t + 1)
168 if k > 0:
169 sender.flooding_point += k
170 else:
171 sender.flooding_point = 0
173 k = sender.flooding_point / 1500
174 if k > 0:
175 msg.reply('刷屏啊?禁言 %d 分钟!' % k)
176 send_to_all_except(sender.jid,
177 (u'%s 已因刷屏而被禁言 %d 分钟。' % (sender.nick, k)) \
178 .encode('utf-8'))
179 log_onoff(sender, BLACK_AUTO % (60 * k))
180 sender.black_before = now + datetime.timedelta(seconds=60*k)
181 sender.put()
182 return
184 sender.last_speak_date = now
185 sender.snooze_before = None
186 try:
187 sender.msg_count += 1
188 sender.msg_chars += len(msg.body)
189 except TypeError:
190 sender.msg_count = 1
191 sender.msg_chars = len(msg.body)
192 sender.put()
193 body = utils.removelinks(msg.body)
194 for u in User.gql('where avail != :1', OFFLINE):
195 if u.snooze_before is not None and u.snooze_before >= now:
196 continue
197 if u.jid == sender.jid:
198 continue
199 try:
200 message = '%s %s' % (
201 u.nick_pattern % sender.nick,
202 body
204 xmpp.send_message(u.jid, message)
205 except xmpp.InvalidJidError:
206 pass
207 log_msg(sender, msg.body)
209 def try_add_user(jid, show=OFFLINE, resource=''):
210 '''使用 memcache 作为锁添加用户'''
211 L = utils.MemLock('add_user')
212 L.require()
213 try:
214 u = get_user_by_jid(jid)
215 if u is not None:
216 return
217 u = add_user(jid, show, resource)
218 finally:
219 L.release()
220 if show != OFFLINE:
221 log_onoff(u, show, resource)
222 logging.info(u'%s added', jid)
224 def add_user(jid, show=OFFLINE, resource=''):
225 '''resource 在 presence type 为 available 里使用'''
226 nick = jid.split('@')[0]
227 old = get_user_by_nick(nick)
228 while old:
229 nick += '_'
230 old = get_user_by_nick(nick)
231 u = User(jid=jid.lower(), avail=show, nick=nick)
232 if show != OFFLINE:
233 u.last_online_date = datetime.datetime.now()
234 if resource:
235 u.resources.append(resource)
236 u.put()
237 log_onoff(u, NEW)
238 logging.info(u'%s 已经加入' % jid)
239 send_to_all_except(jid, u'%s 已经加入' % u.nick)
240 xmpp.send_presence(jid, status=notice)
241 xmpp.send_message(jid, u'欢迎 %s 加入!获取使用帮助,请输入 %shelp' % (
242 u.nick, u.prefix))
243 return u
245 class BasicCommand:
246 handled = True
247 def __init__(self, msg, sender):
248 self.sender = sender
249 self.msg = msg
251 if helpre.match(msg.body):
252 self.do_help()
253 elif msg.body.startswith(sender.prefix):
254 cmd = msg.body[len(sender.prefix):].split()
255 try:
256 handle = getattr(self, 'do_' + cmd[0])
257 except AttributeError:
258 msg.reply(u'错误:未知命令 %s' % cmd[0])
259 except IndexError:
260 msg.reply(u'错误:无命令')
261 except UnicodeEncodeError:
262 msg.reply(u'错误:命令名解码失败。此问题在 GAE 升级其 Python 到 3.x 后方能解决。')
263 else:
264 handle(cmd[1:])
265 logging.debug('%s did command %s' % (sender.jid, cmd[0]))
266 else:
267 self.handled = False
269 def do_online(self, args):
270 '''在线成员列表。可带一个参数,指定在名字中出现的一个子串。'''
271 r = []
272 pat = args[0] if args else None
273 now = datetime.datetime.now()
274 l = User.gql('where avail != :1', OFFLINE)
275 for u in l:
276 m = u.nick
277 if pat and m.find(pat) == -1:
278 continue
279 status = u.avail
280 if status != u'在线':
281 m += u' (%s)' % status
282 if u.snooze_before is not None and u.snooze_before > now:
283 m += u' (snoozing)'
284 if u.black_before is not None and u.black_before > now:
285 m += u' (已禁言)'
286 r.append(unicode('* ' + m))
287 r.sort()
288 n = len(r)
289 if pat:
290 r.insert(0, u'在线成员列表(包含子串 %s):' % pat)
291 r.append(u'共 %d 人。' % n)
292 else:
293 r.insert(0, u'在线成员列表:')
294 r.append(u'共 %d 人在线。' % n)
295 self.msg.reply(u'\n'.join(r).encode('utf-8'))
297 def do_lsadmin(self, args):
298 '''管理员列表'''
299 r = []
300 now = datetime.datetime.now()
301 l = User.gql('where is_admin = :1', True)
302 for u in l:
303 m = u.nick
304 status = u.avail
305 if status != u'在线':
306 m += u' (%s)' % status
307 if u.snooze_before is not None and u.snooze_before > now:
308 m += u' (snoozing)'
309 if u.black_before is not None and u.black_before > now:
310 m += u' (已禁言)'
311 r.append(unicode('* ' + m))
312 r.sort()
313 n = len(r)
314 r.insert(0, u'管理员列表:')
315 r.append(u'共 %d 位管理员。' % n)
316 self.msg.reply(u'\n'.join(r).encode('utf-8'))
318 def do_chatty(self, args):
319 '''消息数排行'''
320 r = []
321 for u in User.gql('ORDER BY msg_chars ASC'):
322 m = u.nick
323 m = u'* %s:\t%5d条,共 %s' % (
324 u.nick, u.msg_count,
325 utils.filesize(u.msg_chars))
326 r.append(m)
327 n = len(r)
328 r.insert(0, u'消息数量排行:')
329 r.append(u'共 %d 人。' % n)
330 self.msg.reply(u'\n'.join(r).encode('utf-8'))
332 def do_nick(self, args):
333 '''更改昵称,需要一个参数,不能使用大部分标点符号'''
334 if len(args) != 1:
335 self.msg.reply('错误:请给出你想到的昵称(不能包含空格)')
336 return
338 q = get_user_by_nick(args[0])
339 if q is not None:
340 self.msg.reply('错误:该昵称已被使用,请使用其它昵称')
341 elif not utils.checkNick(args[0]):
342 self.msg.reply('错误:非法的昵称')
343 else:
344 if not config.nick_can_change and self.sender.nick_changed:
345 self.msg.reply('乖哦,你已经没机会再改昵称了')
346 return
347 old_nick = self.sender.nick
348 log_onoff(self.sender, NICK % (old_nick, args[0]))
349 self.sender.nick = args[0]
350 self.sender.nick_changed = True
351 self.sender.put()
352 send_to_all_except(self.sender.jid,
353 (u'%s 的昵称改成了 %s' % (old_nick, args[0])).encode('utf-8'))
354 self.msg.reply('昵称更改成功!')
355 do_nick.__doc__ += ',最长 %d 字节' % config.nick_maxlen
357 def do_whois(self, args):
358 '''查看用户信息,参数为用户昵称'''
359 if len(args) != 1:
360 self.msg.reply('错误:你想知道关于谁的信息?')
361 return
363 u = get_user_by_nick(args[0])
364 if u is None:
365 self.msg.reply(u'Sorry,查无此人。')
366 return
368 now = datetime.datetime.now()
369 status = u.avail
370 addtime = (u.add_date + timezone).strftime('%Y年%m月%d日 %H时%M分').decode('utf-8')
371 allowpm = u'否' if u.reject_pm else u'是'
372 if u.snooze_before is not None and u.snooze_before > now:
373 status += u' (snoozing)'
374 if u.black_before is not None and u.black_before > now:
375 status += u' (已禁言)'
376 r = []
377 r.append(u'昵称:\t%s' % u.nick)
378 if self.sender.is_admin:
379 r.append(u'JID:\t%s' % u.jid)
380 r.append(u'状态:\t%s' % status)
381 r.append(u'消息数:\t%d' % u.msg_count)
382 r.append(u'消息总量:\t%s' % utils.filesize(u.msg_chars))
383 r.append(u'加入时间:\t%s' % addtime)
384 r.append(u'接收私信:\t%s' % allowpm)
385 r.append(u'自我介绍:\t%s' % u.intro)
386 self.msg.reply(u'\n'.join(r).encode('utf-8'))
388 def do_help(self, args=()):
389 '''显示本帮助。参数 long 显示详细帮助,也可指定命令名。'''
390 doc = []
391 prefix = self.sender.prefix
393 if len(args) > 1:
394 self.msg.reply('参数错误。')
395 return
396 arg = args[0] if args else None
398 if arg is None or arg == 'long':
399 for b in self.__class__.__bases__ + (self.__class__,):
400 for c, f in b.__dict__.items():
401 if c.startswith('do_'):
402 if arg is None:
403 doc.append(u'%s%s:\t%s' % (prefix, c[3:], f.__doc__.decode('utf-8').split(u',', 1)[0].split(u'。', 1)[0]))
404 else:
405 doc.append(u'%s%s:\t%s' % (prefix, c[3:], f.__doc__.decode('utf-8')))
406 doc.sort()
407 if arg is None:
408 doc.insert(0, u'** 命令指南 **\n(当前命令前缀 %s,可设置。使用 %shelp long 显示详细帮助)' % (prefix, prefix))
409 else:
410 doc.insert(0, u'** 命令指南 **\n(当前命令前缀 %s,可设置)' % prefix)
411 doc.append(u'要离开,直接删掉好友即可。')
412 self.msg.reply(u'\n'.join(doc).encode('utf-8'))
413 else:
414 try:
415 handle = getattr(self, 'do_' + arg)
416 except AttributeError:
417 self.msg.reply(u'错误:未知命令 %s' % arg)
418 except UnicodeEncodeError:
419 self.msg.reply(u'错误:命令名解码失败。此问题在 GAE 升级其 Python 到 3.x 后方能解决。')
420 else:
421 self.msg.reply(u'%s%s:\t%s' % (prefix, arg, handle.__doc__.decode('utf-8')))
423 def do_iam(self, args):
424 '''查看自己的信息'''
425 u = self.sender
426 addtime = (u.add_date + timezone).strftime('%Y年%m月%d日 %H时%M分').decode('utf-8')
427 allowpm = u'否' if u.reject_pm else u'是'
428 r = []
429 r.append(u'昵称:\t%s' % u.nick)
430 r.append(u'JID:\t%s' % u.jid)
431 r.append(u'消息数:\t%d' % u.msg_count)
432 r.append(u'消息总量:\t%s' % utils.filesize(u.msg_chars))
433 r.append(u'加入时间:\t%s' % addtime)
434 r.append(u'命令前缀:\t%s' % u.prefix)
435 r.append(u'接收私信:\t%s' % allowpm)
436 r.append(u'自我介绍:\t%s' % u.intro)
437 self.msg.reply(u'\n'.join(r).encode('utf-8'))
439 def do_m(self, args):
440 '''发私信,需要昵称和内容两个参数。私信不会以任何方式被记录。用户可使用 set 命令设置是否接收私信。'''
441 if len(args) < 2:
442 self.msg.reply('请给出昵称和内容。')
443 return
445 target = get_user_by_nick(args[0])
446 if target is None:
447 self.msg.reply('Sorry,查无此人。')
448 return
450 if target.reject_pm:
451 self.msg.reply('很抱歉,对方不接收私信。')
452 return
454 msg = self.msg.body[len(self.sender.prefix):].split(None, 2)[-1]
455 msg = u'_私信_ %s %s' % (target.nick_pattern % self.sender.nick, msg)
456 if xmpp.send_message(target.jid, msg) == xmpp.NO_ERROR:
457 self.msg.reply(u'OK')
458 else:
459 self.msg.reply(u'消息发送失败')
461 def do_intro(self, arg):
462 '''设置自我介绍信息'''
463 if not arg:
464 self.msg.reply('请给出自我介绍的内容。')
465 return
467 msg = self.msg.body[len(self.sender.prefix):].split(None, 1)[-1]
468 u = self.sender
469 try:
470 u.intro = msg
471 except db.BadValueError:
472 # 过长文本已在 handle_message 中被拦截
473 self.msg.reply('错误:自我介绍内容只能为一行。')
474 return
476 u.put()
477 send_to_all_except(u.jid,
478 (u'%s 的新自我介绍:%s' % (u.nick, msg)).encode('utf-8'))
479 self.msg.reply(u'设置成功!')
481 def do_snooze(self, args):
482 '''暂停接收消息,参数为时间(默认单位为秒)。再次发送消息时自动清除'''
483 if len(args) != 1:
484 self.msg.reply('你想停止接收消息多久?')
485 return
486 else:
487 try:
488 n = utils.parseTime(args[0])
489 except ValueError:
490 self.msg.reply('Sorry,我无法理解你说的时间。')
491 return
493 try:
494 self.sender.snooze_before = datetime.datetime.now() + datetime.timedelta(seconds=n)
495 except OverflowError:
496 self.msg.reply('Sorry,你不能睡太久。')
497 return
498 self.sender.put()
499 if n == 0:
500 self.msg.reply('你已经醒来。')
501 else:
502 self.msg.reply('OK,停止接收消息 %d 秒。' % n)
503 log_onoff(self.sender, SNOOZE % n)
505 def do_offline(self, args):
506 '''假装离线,让程序认为你的所有资源已离线。如在你离线时程序仍认为你在线,请使用此命令。'''
507 del self.sender.resources[:]
508 self.sender.avail = OFFLINE
509 self.sender.last_offline_date = datetime.datetime.now()
510 self.sender.put()
511 self.msg.reply('OK,在下次你说你在线之前我都认为你已离线。')
513 def do_fakeresource(self, args):
514 '''假装在线,人工加入一个新的资源,使程序认为你总是在线。使用 offline 命令可删除所有资源的记录。'''
515 self.sender.resources.append('fakeresouce')
516 self.sender.put()
517 self.msg.reply('OK,你将永远在线。')
519 def do_old(self, args):
520 '''聊天记录查询,可选一个数字参数。默认为最后20条。特殊参数 OFFLINE (不区分大小写)显示离线消息(最多 100 条)'''
521 s = self.sender
522 q = False
523 if not args:
524 q = Log.gql("WHERE type = 'chat' ORDER BY time DESC LIMIT 20")
525 elif len(args) == 1:
526 try:
527 n = int(args[0])
528 if n > 0:
529 q = Log.gql("WHERE type = 'chat' ORDER BY time DESC LIMIT %d" % n)
530 except ValueError:
531 if args[0].upper() == 'OFFLINE':
532 q = Log.gql("WHERE time < :1 AND time > :2 AND type = 'chat' ORDER BY time DESC LIMIT 100", s.last_online_date, s.last_offline_date)
533 else:
534 pass
535 if q is not False:
536 r = []
537 q = list(q)
538 q.reverse()
539 if q:
540 if datetime.datetime.today() - q[0].time > datetime.timedelta(hours=24):
541 show_date = True
542 else:
543 show_date = False
544 for l in q:
545 message = '%s %s %s' % (
546 utils.strftime(l.time, timezone, show_date),
547 s.nick_pattern % l.nick,
548 l.msg
550 r.append(message)
551 if r:
552 self.msg.reply(u'\n'.join(r).encode('utf-8'))
553 else:
554 self.msg.reply('没有符合的聊天记录。')
555 else:
556 self.msg.reply('Oops, 参数不正确哦。')
558 def do_set(self, args):
559 '''设置参数。参数格式 key=value;不带参数以查看说明。'''
560 #注意:选项名/值中不能包含空格
561 if len(args) != 1:
562 doc = []
563 for c, f in self.__class__.__dict__.items():
564 if c.startswith('set_'):
565 doc.append(u'* %s:\t%s' % (c[4:], f.__doc__.decode('utf-8')))
566 for b in self.__class__.__bases__:
567 for c, f in b.__dict__.items():
568 if c.startswith('set_'):
569 doc.append(u'* %s:\t%s' % (c[4:], f.__doc__.decode('utf-8')))
570 doc.sort()
571 doc.insert(0, u'设置选项:')
572 self.msg.reply(u'\n'.join(doc).encode('utf-8'))
573 else:
574 msg = self.msg
575 cmd = msg.body.split(None, 1)[1].split('=', 1)
576 if len(cmd) == 1:
577 msg.reply(u'错误:请给出选项值')
578 return
579 try:
580 handle = getattr(self, 'set_' + cmd[0])
581 except AttributeError:
582 msg.reply(u'错误:未知选项 %s' % cmd[0])
583 except IndexError:
584 msg.reply(u'错误:无选项')
585 except UnicodeEncodeError:
586 msg.reply(u'错误:选项名解码失败。此问题在 GAE 升级其 Python 到 3.x 后方能解决。')
587 else:
588 handle(cmd[1])
590 def set_prefix(self, arg):
591 '''设置命令前缀'''
592 self.sender.prefix = arg
593 self.sender.put()
594 self.msg.reply(u'设置成功!')
596 def set_nickpattern(self, arg):
597 '''设置昵称显示格式,用 %s 表示昵称的位置'''
598 try:
599 arg % 'test'
600 except (TypeError, ValueError):
601 self.msg.reply(u'错误:不正确的格式')
602 return
604 self.sender.nick_pattern = arg
605 self.sender.put()
606 self.msg.reply(u'设置成功!')
608 def set_allowpm(self, arg):
609 '''设置是否接收私信,参数为 y(接收)或者 n(拒绝)'''
610 if arg not in 'yn':
611 self.msg.reply(u'错误的参数。')
612 return
614 if arg == 'y':
615 self.sender.reject_pm = False
616 else:
617 self.sender.reject_pm = True
618 self.sender.put()
619 self.msg.reply(u'设置成功!')
621 class AdminCommand(BasicCommand):
622 def do_kick(self, args):
623 '''删除某人。他仍可以重新加入。'''
624 if len(args) != 1:
625 self.msg.reply('请给出昵称。')
626 return
628 target = get_user_by_nick(args[0])
629 if target is None:
630 self.msg.reply('Sorry,查无此人。')
631 return
633 if target.jid == config.root:
634 self.msg.reply('不能删除 root 用户')
635 return
637 targetjid = target.jid
638 targetnick = target.nick
639 target.delete()
640 self.msg.reply((u'OK,删除 %s。' % target.nick).encode('utf-8'))
641 send_to_all_except(self.sender.jid, (u'%s 已被删除。' % self.sender.nick) \
642 .encode('utf-8'))
643 xmpp.send_message(targetjid, u'你已被管理员从此群中删除,请删除该好友。')
644 log_onoff(self.sender, KICK % (targetnick, targetjid))
646 def do_quiet(self, args):
647 '''禁言某人,参数为昵称和时间(默认单位秒)'''
648 if len(args) != 2:
649 self.msg.reply('请给出昵称和时间。')
650 return
651 else:
652 try:
653 n = utils.parseTime(args[1])
654 except ValueError:
655 self.msg.reply('Sorry,我无法理解你说的时间。')
656 return
658 target = get_user_by_nick(args[0])
659 if target is None:
660 self.msg.reply('Sorry,查无此人。')
661 return
663 target.black_before = datetime.datetime.now() + datetime.timedelta(seconds=n)
664 target.put()
665 self.msg.reply((u'OK,禁言 %s %d 秒。' % (target.nick, n)).encode('utf-8'))
666 send_to_all_except((self.sender.jid, target.jid),
667 (u'%s 已被禁言 %s 秒。' % (target.nick, n)) \
668 .encode('utf-8'))
669 xmpp.send_message(target.jid, u'你已被管理员禁言 %d 秒。' % n)
670 log_onoff(self.sender, BLACK % (target.nick, n))
672 def do_admin(self, args):
673 '''将某人添加为管理员'''
674 if len(args) != 1:
675 self.msg.reply(u'请给出昵称。')
676 return
678 target = get_user_by_nick(args[0])
679 if target is None:
680 self.msg.reply(u'Sorry,查无此人。')
681 return
683 if target.is_admin:
684 self.msg.reply(u'%s 已经是管理员了。' % target.nick)
685 return
687 target.is_admin = True
688 target.put()
689 send_to_all_except(target.jid,
690 (u'%s 已成为管理员。' % target.nick) \
691 .encode('utf-8'))
692 xmpp.send_message(target.jid, u'你已是本群管理员。')
693 log_onoff(self.sender, ADMIN % (target.nick, self.sender.nick))
695 def do_unadmin(self, args):
696 '''取消某人管理员的权限'''
697 if len(args) != 1:
698 self.msg.reply(u'请给出昵称。')
699 return
701 target = get_user_by_nick(args[0])
702 if target is None:
703 self.msg.reply(u'Sorry,查无此人。')
704 return
706 if not target.is_admin:
707 self.msg.reply(u'%s 不是管理员。' % target.nick)
708 return
710 target.is_admin = False
711 target.put()
712 send_to_all_except(target.jid,
713 (u'%s 已不再是管理员。' % target.nick) \
714 .encode('utf-8'))
715 xmpp.send_message(target.jid, u'你已不再是本群管理员。')
716 log_onoff(self.sender, UNADMIN % (target.nick, self.sender.nick))