2 # vim:fileencoding=utf-8
8 from google
.appengine
.ext
import db
9 from google
.appengine
.api
import xmpp
14 helpre
= re
.compile(r
'^\W{0,2}help$')
26 NICK
= u
'昵称更改 (%s -> %s)'
27 SNOOZE
= u
'snooze %ds'
29 BLACK_AUTO
= u
'被禁言 %ds'
31 ADMIN
= u
'%s 成为管理员 (by %s)'
32 UNADMIN
= u
'%s 不再是管理员 (by %s)'
34 BLOCK
= u
'封禁 %s,原因:%s'
46 STATUS_LIST
= [CHAT
, ONLINE
, AWAY
, XAWAY
, BUSY
, OFFLINE
]
48 timezone
= datetime
.timedelta(hours
=config
.timezoneoffset
)
51 jid
= db
.StringProperty(required
=True, indexed
=True)
52 nick
= db
.StringProperty(required
=True, indexed
=True)
54 add_date
= db
.DateTimeProperty(auto_now_add
=True)
55 last_online_date
= db
.DateTimeProperty()
56 last_offline_date
= db
.DateTimeProperty()
57 last_speak_date
= db
.DateTimeProperty()
59 msg_count
= db
.IntegerProperty(required
=True, default
=0)
60 msg_chars
= db
.IntegerProperty(required
=True, default
=0)
61 credit
= db
.IntegerProperty(required
=True, default
=0)
63 black_before
= db
.DateTimeProperty(auto_now_add
=True)
64 snooze_before
= db
.DateTimeProperty()
65 flooding_point
= db
.IntegerProperty(default
=0)
67 avail
= db
.StringProperty(required
=True)
68 is_admin
= db
.BooleanProperty(required
=True, default
=False)
69 resources
= db
.StringListProperty(required
=True)
71 reject_pm
= db
.BooleanProperty(default
=False)
73 prefix
= db
.StringProperty(required
=True, default
=config
.default_prefix
)
74 nick_pattern
= db
.StringProperty(required
=True, default
='[%s]')
75 nick_changed
= db
.BooleanProperty(required
=True, default
=False)
76 intro
= db
.StringProperty()
79 time
= db
.DateTimeProperty(auto_now_add
=True, indexed
=True)
80 msg
= db
.StringProperty(required
=True, multiline
=True)
81 jid
= db
.StringProperty()
82 nick
= db
.StringProperty()
83 type = db
.StringProperty(required
=True, indexed
=True,
84 choices
=set(['chat', 'member', 'admin', 'misc']))
86 class Group(db
.Model
):
87 time
= db
.DateTimeProperty(auto_now_add
=True)
88 topic
= db
.StringProperty(multiline
=True)
89 status
= db
.StringProperty()
91 class BlockedUser(db
.Model
):
92 jid
= db
.StringProperty(required
=True, indexed
=True)
93 add_date
= db
.DateTimeProperty(auto_now_add
=True)
94 reason
= db
.StringProperty()
96 def log_msg(sender
, msg
):
97 l
= Log(jid
=sender
.jid
, nick
=sender
.nick
,
101 def log_onoff(sender
, action
, resource
=''):
103 if action
== OFFLINE
and not sender
.resources
:
104 msg
= u
'完全%s (%s)' % (action
, resource
)
106 msg
= '%s (%s)' % (action
, resource
)
109 l
= Log(jid
=sender
.jid
, nick
=sender
.nick
,
110 type='member', msg
=msg
)
113 def log_admin(sender
, action
):
114 l
= Log(jid
=sender
.jid
, nick
=sender
.nick
,
115 type='admin', msg
=action
)
118 def get_user_by_jid(jid
):
119 return User
.gql('where jid = :1', jid
.lower()).get()
121 def get_user_by_nick(nick
):
122 return User
.gql('where nick = :1', nick
).get()
124 def get_group_info():
125 return Group
.all().get()
127 def get_blocked_user(jid
):
128 return BlockedUser
.gql('where jid = :1', jid
.lower()).get()
130 def get_member_list():
131 now
= datetime
.datetime
.now()
133 l
= User
.gql('where avail != :1', OFFLINE
)
134 return [unicode(x
.jid
) for x
in l \
135 if x
.snooze_before
is None or x
.snooze_before
< now
]
137 def send_to_all_except(jid
, message
):
138 if isinstance(jid
, str):
139 jids
= [x
for x
in get_member_list() if x
!= jid
]
141 jids
= [x
for x
in get_member_list() if x
not in jid
]
145 xmpp
.send_message(jids
, message
)
146 except xmpp
.InvalidJidError
:
149 def send_to_all(message
):
150 jids
= get_member_list()
151 xmpp
.send_message(jids
, message
)
153 def send_status(jid
):
154 if get_blocked_user(jid
):
155 xmpp
.send_presence(jid
, status
=u
'您已经被本群封禁')
158 grp
= get_group_info()
159 if grp
is None or not grp
.status
:
160 xmpp
.send_presence(jid
)
162 xmpp
.send_presence(jid
, status
=grp
.status
)
164 def handle_message(msg
):
165 jid
= msg
.sender
.split('/')[0]
167 sender
= get_blocked_user(jid
)
168 if sender
is not None:
169 msg
.reply(u
'您已经被本群封禁,原因为 %s。' % sender
.reason
)
172 sender
= get_user_by_jid(jid
)
174 msg
.reply('很抱歉,出错了,请尝试更改状态或者重新添加好友。')
177 if msg
.body
.startswith('?OTR:'):
178 msg
.reply('不支持 OTR 加密!')
181 if msg
.body
in config
.blocked_away_messages
:
182 msg
.reply('系统认为您的客户端正在自动发送离开消息。如果您认为这并不正确,请向管理员反馈。')
185 if len(msg
.body
) > 500 or msg
.body
.count('\n') > 5:
186 msgbody
= utils
.post_code(msg
.body
)
188 msg
.reply(u
'内容过长,已贴至 %s' % msgbody
)
190 logging
.warn(u
'贴代码失败,代码长度 %d' % len(msg
.body
))
191 msg
.reply('由于技术限制,每条消息最长为 500 字。大段文本请贴 paste 网站。\n'
192 '如 http://paste.ubuntu.org.cn/ http://slexy.org/\n'
198 if sender
.is_admin
or sender
.jid
== config
.root
:
199 ch
= AdminCommand(msg
, sender
)
201 ch
= BasicCommand(msg
, sender
)
203 if not ch
or not ch
.handled
:
204 now
= datetime
.datetime
.now()
205 if sender
.black_before
is not None \
206 and sender
.black_before
> now
:
207 if (datetime
.datetime
.today()+timezone
).date() == \
208 (sender
.black_before
+timezone
).date():
211 format
= '%m月%d日 %H时%M分%S秒'
212 msg
.reply('你已被禁言至 ' \
213 + (sender
.black_before
+timezone
).strftime(format
))
216 if sender
.last_speak_date
is not None:
217 d
= now
- sender
.last_speak_date
219 if d
.days
> 0 or t
> 60:
220 sender
.flooding_point
= 0
222 k
= 1000 / (t
* t
+ 1)
224 sender
.flooding_point
+= k
226 sender
.flooding_point
= 0
228 k
= sender
.flooding_point
/ 1500
230 msg
.reply('刷屏啊?禁言 %d 分钟!' % k
)
231 send_to_all_except(sender
.jid
,
232 (u
'%s 已因刷屏而被禁言 %d 分钟。' % (sender
.nick
, k
)) \
234 log_onoff(sender
, BLACK_AUTO
% (60 * k
))
235 sender
.black_before
= now
+ datetime
.timedelta(seconds
=60*k
)
239 sender
.last_speak_date
= now
240 sender
.snooze_before
= None
242 sender
.msg_count
+= 1
243 sender
.msg_chars
+= len(msgbody
)
246 sender
.msg_chars
= len(msgbody
)
248 body
= utils
.removelinks(msgbody
)
249 for u
in User
.gql('where avail != :1', OFFLINE
):
250 if u
.snooze_before
is not None and u
.snooze_before
>= now
:
252 if u
.jid
== sender
.jid
:
255 message
= '%s %s' % (
256 u
.nick_pattern
% sender
.nick
,
259 xmpp
.send_message(u
.jid
, message
)
260 except xmpp
.InvalidJidError
:
262 log_msg(sender
, msgbody
)
264 def try_add_user(jid
, show
=OFFLINE
, resource
=''):
265 '''使用 memcache 作为锁添加用户'''
266 u
= get_blocked_user(jid
)
268 xmpp
.send_message(jid
, u
'您已经被本群封禁,原因为 %s。' % u
.reason
)
271 L
= utils
.MemLock('add_user')
274 u
= get_user_by_jid(jid
)
277 u
= add_user(jid
, show
, resource
)
281 log_onoff(u
, show
, resource
)
282 logging
.info(u
'%s added', jid
)
284 def add_user(jid
, show
=OFFLINE
, resource
=''):
285 '''resource 在 presence type 为 available 里使用'''
286 nick
= jid
.split('@')[0]
287 # same user name with different domains are possible
288 while get_user_by_nick(nick
):
290 u
= User(jid
=jid
.lower(), avail
=show
, nick
=nick
)
292 u
.last_online_date
= datetime
.datetime
.now()
294 u
.resources
.append(resource
)
297 logging
.info(u
'%s 已经加入' % jid
)
299 xmpp
.send_message(jid
, u
'欢迎 %s 加入!要获取使用帮助,请输入 %shelp,要获知群主题,请输入 %stopic。' % (
300 u
.nick
, u
.prefix
, u
.prefix
))
303 def del_user(jid
, by_cmd
=False):
304 l
= User
.gql('where jid = :1', jid
.lower())
306 if u
.jid
== config
.root
:
307 xmpp
.send_message(jid
, u
'root 用户:离开前请确定你已做好善后工作!')
310 logging
.info(u
'%s (%s) 已经离开 (通过使用命令)' % (u
.nick
, u
.jid
))
312 logging
.info(u
'%s (%s) 已经离开' % (u
.nick
, u
.jid
))
317 def __init__(self
, msg
, sender
):
321 if helpre
.match(msg
.body
):
323 elif msg
.body
.startswith(sender
.prefix
):
324 cmd
= msg
.body
[len(sender
.prefix
):].split()
326 handle
= getattr(self
, 'do_' + cmd
[0])
327 except AttributeError:
328 msg
.reply(u
'错误:未知命令 %s' % cmd
[0])
331 except UnicodeEncodeError:
332 msg
.reply(u
'错误:命令名解码失败。此问题在 GAE 升级其 Python 到 3.x 后方能解决。')
335 logging
.debug('%s did command %s' % (sender
.jid
, cmd
[0]))
339 def get_msg_part(self
, index
):
340 '''返回消息中第 index 个单词及其后的所有字符'''
341 return self
.msg
.body
[len(self
.sender
.prefix
):].split(None, index
)[-1]
343 def do_online(self
, args
):
344 '''在线成员列表。可带一个参数,指定在名字中出现的一个子串。'''
346 pat
= args
[0] if args
else None
347 now
= datetime
.datetime
.now()
348 l
= User
.gql('where avail != :1', OFFLINE
)
351 if pat
and m
.find(pat
) == -1:
355 m
+= u
' (%s)' % status
356 if u
.snooze_before
is not None and u
.snooze_before
> now
:
358 if u
.black_before
is not None and u
.black_before
> now
:
360 r
.append(unicode('* ' + m
))
364 r
.insert(0, u
'在线成员列表(包含子串 %s):' % pat
)
365 r
.append(u
'共 %d 人。' % n
)
367 r
.insert(0, u
'在线成员列表:')
368 r
.append(u
'共 %d 人在线。' % n
)
369 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
371 def do_lsadmin(self
, args
):
374 now
= datetime
.datetime
.now()
375 l
= User
.gql('where is_admin = :1', True)
380 m
+= u
' (%s)' % status
381 if u
.snooze_before
is not None and u
.snooze_before
> now
:
383 if u
.black_before
is not None and u
.black_before
> now
:
385 r
.append(unicode('* ' + m
))
388 r
.insert(0, u
'管理员列表:')
389 r
.append(u
'共 %d 位管理员。' % n
)
390 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
392 def do_lsblocked(self
, args
):
395 l
= BlockedUser
.all()
397 r
.append(unicode('* %s (%s, %s)' % (u
.jid
,
398 utils
.strftime(u
.add_date
, timezone
),
404 r
.insert(0, u
'封禁列表:')
405 r
.append(u
'共 %d 个 JID 被封禁。' % n
)
406 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
408 def do_chatty(self
, args
):
411 for u
in User
.gql('ORDER BY msg_count ASC'):
413 m
= u
'* %s:\t%5d条,共 %s' % (
415 utils
.filesize(u
.msg_chars
))
418 r
.insert(0, u
'消息数量排行:')
419 r
.append(u
'共 %d 人。' % n
)
420 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
422 def do_nick(self
, args
):
423 '''更改昵称,需要一个参数,不能使用大部分标点符号'''
425 self
.msg
.reply('错误:请给出你想到的昵称(不能包含空格)')
428 q
= get_user_by_nick(args
[0])
430 self
.msg
.reply('错误:该昵称已被使用,请使用其它昵称')
431 elif not utils
.checkNick(args
[0]):
432 self
.msg
.reply('错误:非法的昵称')
434 if not config
.nick_can_change
and self
.sender
.nick_changed
:
435 self
.msg
.reply('乖哦,你已经没机会再改昵称了')
437 old_nick
= self
.sender
.nick
438 log_onoff(self
.sender
, NICK
% (old_nick
, args
[0]))
439 self
.sender
.nick
= args
[0]
440 self
.sender
.nick_changed
= True
442 send_to_all_except(self
.sender
.jid
,
443 (u
'%s 的昵称改成了 %s' % (old_nick
, args
[0])).encode('utf-8'))
444 self
.msg
.reply('昵称更改成功!')
445 do_nick
.__doc
__ += ',最长 %d 字节' % config
.nick_maxlen
447 def do_whois(self
, args
):
450 self
.msg
.reply('错误:你想知道关于谁的信息?')
453 u
= get_user_by_nick(args
[0])
455 self
.msg
.reply(u
'Sorry,查无此人。')
458 now
= datetime
.datetime
.now()
460 addtime
= (u
.add_date
+ timezone
).strftime('%Y年%m月%d日 %H时%M分').decode('utf-8')
461 allowpm
= u
'否' if u
.reject_pm
else u
'是'
462 if u
.snooze_before
is not None and u
.snooze_before
> now
:
463 status
+= u
' (snoozing)'
464 if u
.black_before
is not None and u
.black_before
> now
:
467 r
.append(u
'昵称:\t%s' % u
.nick
)
468 if self
.sender
.is_admin
:
469 r
.append(u
'JID:\t%s' % u
.jid
)
470 r
.append(u
'状态:\t%s' % status
)
471 r
.append(u
'消息数:\t%d' % u
.msg_count
)
472 r
.append(u
'消息总量:\t%s' % utils
.filesize(u
.msg_chars
))
473 r
.append(u
'加入时间:\t%s' % addtime
)
474 r
.append(u
'接收私信:\t%s' % allowpm
)
475 r
.append(u
'自我介绍:\t%s' % u
.intro
)
476 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
478 def do_help(self
, args
=()):
479 '''显示本帮助。参数 long 显示详细帮助,也可指定命令名。'''
481 prefix
= self
.sender
.prefix
484 self
.msg
.reply('参数错误。')
486 arg
= args
[0] if args
else None
488 if arg
is None or arg
== 'long':
489 for b
in self
.__class
__.__bases
__ + (self
.__class
__,):
490 for c
, f
in b
.__dict
__.items():
491 if c
.startswith('do_'):
493 doc
.append(u
'%s%s:\t%s' % (prefix
, c
[3:], f
.__doc
__.decode('utf-8').\
494 split(u
',', 1)[0].split(u
'。', 1)[0]))
496 doc
.append(u
'%s%s:\t%s' % (prefix
, c
[3:], f
.__doc
__.decode('utf-8')))
499 doc
.insert(0, u
'** 命令指南 **\n(当前命令前缀 %s,可设置。使用 %shelp long 显示详细帮助)' % (prefix
, prefix
))
501 doc
.insert(0, u
'** 命令指南 **\n(当前命令前缀 %s,可设置)' % prefix
)
502 doc
.append(u
'要离开,直接删掉好友即可。')
503 doc
.append(u
'Gtalk 客户端用户要离开请使用 quit 命令。')
504 self
.msg
.reply(u
'\n'.join(doc
).encode('utf-8'))
507 handle
= getattr(self
, 'do_' + arg
)
508 except AttributeError:
509 self
.msg
.reply(u
'错误:未知命令 %s' % arg
)
510 except UnicodeEncodeError:
511 self
.msg
.reply(u
'错误:命令名解码失败。此问题在 GAE 升级其 Python 到 3.x 后方能解决。')
513 self
.msg
.reply(u
'%s%s:\t%s' % (prefix
, arg
, handle
.__doc
__.decode('utf-8')))
515 def do_topic(self
, args
=()):
517 grp
= get_group_info()
518 if grp
is None or not grp
.topic
:
519 self
.msg
.reply(u
'没有设置群主题。')
521 self
.msg
.reply(grp
.topic
)
523 def do_iam(self
, args
):
526 addtime
= (u
.add_date
+ timezone
).strftime('%Y年%m月%d日 %H时%M分').decode('utf-8')
527 allowpm
= u
'否' if u
.reject_pm
else u
'是'
529 r
.append(u
'昵称:\t%s' % u
.nick
)
530 r
.append(u
'JID:\t%s' % u
.jid
)
531 r
.append(u
'资源:\t%s' % u
' '.join(u
.resources
))
532 r
.append(u
'消息数:\t%d' % u
.msg_count
)
533 r
.append(u
'消息总量:\t%s' % utils
.filesize(u
.msg_chars
))
534 r
.append(u
'加入时间:\t%s' % addtime
)
535 r
.append(u
'命令前缀:\t%s' % u
.prefix
)
536 r
.append(u
'接收私信:\t%s' % allowpm
)
537 r
.append(u
'自我介绍:\t%s' % u
.intro
)
538 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
540 def do_m(self
, args
):
541 '''发私信,需要昵称和内容两个参数。私信不会以任何方式被记录。用户可使用 set 命令设置是否接收私信。'''
543 self
.msg
.reply('请给出昵称和内容。')
546 target
= get_user_by_nick(args
[0])
548 self
.msg
.reply('Sorry,查无此人。')
552 self
.msg
.reply('很抱歉,对方不接收私信。')
555 msg
= self
.get_msg_part(2)
556 msg
= u
'_私信_ %s %s' % (target
.nick_pattern
% self
.sender
.nick
, msg
)
557 if xmpp
.send_message(target
.jid
, msg
) == xmpp
.NO_ERROR
:
558 self
.msg
.reply(u
'OK')
560 self
.msg
.reply(u
'消息发送失败')
562 def do_intro(self
, arg
):
565 self
.msg
.reply('请给出自我介绍的内容。')
568 msg
= self
.get_msg_part(1)
572 except db
.BadValueError
:
573 # 过长文本已在 handle_message 中被拦截
574 self
.msg
.reply('错误:自我介绍内容只能为一行。')
578 self
.msg
.reply(u
'设置成功!')
580 def do_snooze(self
, args
):
581 '''暂停接收消息,参数为时间(默认单位为秒)。再次发送消息时自动清除'''
583 self
.msg
.reply('你想停止接收消息多久?')
587 n
= utils
.parseTime(args
[0])
589 self
.msg
.reply('Sorry,我无法理解你说的时间。')
593 self
.sender
.snooze_before
= datetime
.datetime
.now() + datetime
.timedelta(seconds
=n
)
594 except OverflowError:
595 self
.msg
.reply('Sorry,你不能睡太久。')
599 self
.msg
.reply('你已经醒来。')
601 self
.msg
.reply(u
'OK,停止接收消息 %s。' % utils
.displayTime(n
))
602 log_onoff(self
.sender
, SNOOZE
% n
)
604 def do_offline(self
, args
):
605 '''假装离线,让程序认为你的所有资源已离线。如在你离线时程序仍认为你在线,请使用此命令。'''
606 del self
.sender
.resources
[:]
607 self
.sender
.avail
= OFFLINE
608 self
.sender
.last_offline_date
= datetime
.datetime
.now()
610 self
.msg
.reply('OK,在下次你说你在线之前我都认为你已离线。')
612 def do_fakeresource(self
, args
):
613 '''假装在线,人工加入一个新的资源,使程序认为你总是在线。使用 off 参数来取消。'''
614 if args
and args
[0] == 'off':
616 self
.sender
.resources
.remove('fakeresouce')
617 self
.msg
.reply('OK,fakeresouce 已取消。')
620 self
.msg
.reply('没有设置 fakeresouce。')
623 self
.sender
.resources
.index('fakeresouce')
624 self
.msg
.reply('你已经设置了永远在线。')
626 self
.sender
.resources
.append('fakeresouce')
627 self
.msg
.reply('OK,你将永远在线。')
630 def do_old(self
, args
):
631 '''聊天记录查询,可选一个数字参数。默认为最后20条。特殊参数 OFFLINE (不区分大小写)显示离线消息(最多 100 条)'''
635 q
= Log
.gql("WHERE type = 'chat' ORDER BY time DESC LIMIT 20")
640 q
= Log
.gql("WHERE type = 'chat' ORDER BY time DESC LIMIT %d" % n
)
642 if args
[0].upper() == 'OFFLINE':
643 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
)
651 if datetime
.datetime
.today() - q
[0].time
> datetime
.timedelta(hours
=24):
656 message
= '%s %s %s' % (
657 utils
.strftime(l
.time
, timezone
, show_date
),
658 s
.nick_pattern
% l
.nick
,
663 self
.msg
.reply(u
'\n'.join(r
).encode('utf-8'))
665 self
.msg
.reply('没有符合的聊天记录。')
667 self
.msg
.reply('Oops, 参数不正确哦。')
669 def do_set(self
, args
):
670 '''设置参数。参数格式 key=value;不带参数以查看说明。'''
674 for c
, f
in self
.__class
__.__dict
__.items():
675 if c
.startswith('set_'):
676 doc
.append(u
'* %s:\t%s' % (c
[4:], f
.__doc
__.decode('utf-8')))
677 for b
in self
.__class
__.__bases
__:
678 for c
, f
in b
.__dict
__.items():
679 if c
.startswith('set_'):
680 doc
.append(u
'* %s:\t%s' % (c
[4:], f
.__doc
__.decode('utf-8')))
682 doc
.insert(0, u
'设置选项:')
683 self
.msg
.reply(u
'\n'.join(doc
).encode('utf-8'))
686 cmd
= msg
.body
.split(None, 1)[1].split('=', 1)
688 msg
.reply(u
'错误:请给出选项值')
691 handle
= getattr(self
, 'set_' + cmd
[0])
692 except AttributeError:
693 msg
.reply(u
'错误:未知选项 %s' % cmd
[0])
696 except UnicodeEncodeError:
697 msg
.reply(u
'错误:选项名解码失败。此问题在 GAE 升级其 Python 到 3.x 后方能解决。')
701 def do_quit(self
, args
):
702 '''删除用户数据。某些自称“不作恶”的公司的客户端会不按协议要求发送删除好友的消息,请 gtalk 官方客户端用户使用此命令退出。参见 http://xmpp.org/rfcs/rfc3921.html#rfc.section.6.3 。'''
703 del_user(self
.sender
.jid
)
704 self
.msg
.reply(u
'OK.')
706 def set_prefix(self
, arg
):
708 self
.sender
.prefix
= arg
710 self
.msg
.reply(u
'设置成功!')
712 def set_nickpattern(self
, arg
):
713 '''设置昵称显示格式,用 %s 表示昵称的位置'''
716 except (TypeError, ValueError):
717 self
.msg
.reply(u
'错误:不正确的格式')
720 self
.sender
.nick_pattern
= arg
722 self
.msg
.reply(u
'设置成功!')
724 def set_allowpm(self
, arg
):
725 '''设置是否接收私信,参数为 y(接收)或者 n(拒绝)'''
727 self
.msg
.reply(u
'错误的参数。')
731 self
.sender
.reject_pm
= False
733 self
.sender
.reject_pm
= True
735 self
.msg
.reply(u
'设置成功!')
737 class AdminCommand(BasicCommand
):
738 def do_kick(self
, args
):
741 self
.msg
.reply('请给出昵称。')
744 target
= get_user_by_nick(args
[0])
746 self
.msg
.reply('Sorry,查无此人。')
749 if target
.jid
== config
.root
:
750 self
.msg
.reply('不能删除 root 用户')
753 targetjid
= target
.jid
754 targetnick
= target
.nick
756 self
.msg
.reply((u
'OK,删除 %s。' % target
.nick
).encode('utf-8'))
757 send_to_all_except(self
.sender
.jid
, (u
'%s 已被删除。' % target
.nick
) \
759 xmpp
.send_message(targetjid
, u
'你已被管理员从此群中删除,请删除该好友。')
760 log_admin(self
.sender
, KICK
% (targetnick
, targetjid
))
762 def do_quiet(self
, args
):
763 '''禁言某人,参数为昵称和时间(默认单位秒)'''
765 self
.msg
.reply('请给出昵称和时间。')
769 n
= utils
.parseTime(args
[1])
771 self
.msg
.reply('Sorry,我无法理解你说的时间。')
774 target
= get_user_by_nick(args
[0])
776 self
.msg
.reply('Sorry,查无此人。')
779 target
.black_before
= datetime
.datetime
.now() + datetime
.timedelta(seconds
=n
)
781 self
.msg
.reply(u
'OK,禁言 %s %s。' % (target
.nick
, utils
.displayTime(n
)))
782 send_to_all_except((self
.sender
.jid
, target
.jid
),
783 (u
'%s 已被禁言 %s。' % (target
.nick
, utils
.displayTime(n
))) \
785 xmpp
.send_message(target
.jid
, u
'你已被管理员禁言 %s。' % utils
.displayTime(n
))
786 log_admin(self
.sender
, BLACK
% (target
.nick
, n
))
788 def do_notice(self
, arg
):
789 '''发送群通告。只会发给在线的人,包括 snoozing 者。'''
791 self
.msg
.reply('请给出群通告的内容。')
794 msg
= self
.msg
.body
[len(self
.sender
.prefix
):].split(None, 1)[-1]
796 l
= User
.gql('where avail != :1', OFFLINE
)
797 log_admin(self
.sender
, NOTICE
% msg
)
800 xmpp
.send_message(u
.jid
, u
'通告:' + msg
)
801 except xmpp
.InvalidJidError
:
804 def do_topic(self
, args
=()):
806 grp
= get_group_info()
808 if grp
is None or not grp
.topic
:
809 self
.msg
.reply(u
'没有设置群主题。')
811 self
.msg
.reply(grp
.topic
)
813 grp
= get_group_info()
816 grp
.topic
= self
.msg
.body
[len(self
.sender
.prefix
):].split(None, 1)[-1]
818 self
.msg
.reply(u
'群主题已更新。')
820 def do_admin(self
, args
):
823 self
.msg
.reply(u
'请给出昵称。')
826 target
= get_user_by_nick(args
[0])
828 self
.msg
.reply(u
'Sorry,查无此人。')
832 self
.msg
.reply(u
'%s 已经是管理员了。' % target
.nick
)
835 target
.is_admin
= True
837 send_to_all_except(target
.jid
,
838 (u
'%s 已成为管理员。' % target
.nick
) \
840 xmpp
.send_message(target
.jid
, u
'你已是本群管理员。')
841 log_admin(self
.sender
, ADMIN
% (target
.nick
, self
.sender
.nick
))
843 def do_unadmin(self
, args
):
846 self
.msg
.reply(u
'请给出昵称。')
849 target
= get_user_by_nick(args
[0])
851 self
.msg
.reply(u
'Sorry,查无此人。')
854 if not target
.is_admin
:
855 self
.msg
.reply(u
'%s 不是管理员。' % target
.nick
)
858 target
.is_admin
= False
860 send_to_all_except(target
.jid
,
861 (u
'%s 已不再是管理员。' % target
.nick
) \
863 xmpp
.send_message(target
.jid
, u
'你已不再是本群管理员。')
864 log_admin(self
.sender
, UNADMIN
% (target
.nick
, self
.sender
.nick
))
866 def do_block(self
, args
):
867 '''封禁某个 ID,参数为用户昵称或者 ID(如果不是已经加入的 ID 的话),以及封禁原因'''
869 self
.msg
.reply(u
'请给出要封禁的用户和原因。')
872 target
= get_user_by_nick(args
[0])
873 reason
= self
.msg
.body
[len(self
.sender
.prefix
):].split(None, 2)[-1]
881 fullname
= '%s (%s)' % (name
, jid
)
882 u
= BlockedUser
.gql('where jid = :1', jid
).get()
884 self
.msg
.reply(u
'此 JID 已经被封禁。')
887 if jid
== config
.root
:
888 self
.msg
.reply('不能封禁 root 用户')
893 u
= BlockedUser(jid
=jid
, reason
=reason
)
896 send_to_all_except(self
.sender
.jid
,
897 (u
'%s 已被本群封禁,理由为 %s。' % (name
, reason
)) \
899 self
.msg
.reply(u
'%s 已被本群封禁,理由为 %s。' % (fullname
, reason
))
900 xmpp
.send_message(jid
, u
'你已被本群封禁,理由为 %s。' % reason
)
901 xmpp
.send_presence(jid
, status
=u
'您已经被本群封禁')
902 log_admin(self
.sender
, BLOCK
% (fullname
, reason
))
904 def do_unblock(self
, args
):
907 self
.msg
.reply(u
'请给出要解封用户的 JID。')
910 target
= get_blocked_user(args
[0])
912 self
.msg
.reply(u
'封禁列表中没有这个 JID。')
916 send_to_all((u
'%s 已被解除封禁。' % args
[0]) \
918 log_admin(self
.sender
, UNBLOCK
% args
[0])
920 def do_groupstatus(self
, arg
):
922 grp
= get_group_info()
925 grp
.status
= self
.msg
.body
[len(self
.sender
.prefix
):].split(None, 1)[-1]
928 xmpp
.send_presence(u
.jid
, status
=grp
.status
)
929 self
.msg
.reply(u
'设置成功!')