forked from coldnight/clubot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
clubot.py
246 lines (217 loc) · 8.52 KB
/
clubot.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
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Author : cold
# Email : wh_linux@126.com
#
# 2012-10-10 12:00
# * 使用pyxmpp2重写
# 2012-10-29 14:25
# * 修复自动下线和发送系统消息的bug
# * 修复用户离群的bug
# + 添加多线程处理消息
# 2012-10-30 16:00
# + 添加连接之前清除状态表
# 2012-11-19 14:00
# * 修复断开自动重启
#
from __future__ import absolute_import
import logging
import sys, os
import traceback
import pyxmpp2
from pyxmpp2.jid import JID
from pyxmpp2.presence import Presence
from pyxmpp2.client import Client
from pyxmpp2.settings import XMPPSettings
from pyxmpp2.interfaces import EventHandler, event_handler, QUIT
from pyxmpp2.streamevents import DisconnectedEvent,ConnectedEvent
from pyxmpp2.roster import RosterReceivedEvent, RosterUpdatedEvent
from pyxmpp2.interfaces import XMPPFeatureHandler
from pyxmpp2.interfaces import presence_stanza_handler, message_stanza_handler
from pyxmpp2.ext.version import VersionProvider
from logics import Logics
from message import MessageBus
from mtornado import TornadoMainLoop
from utility import welcome, new_member, get_logger
from settings import USER, PASSWORD, STATUS, IMPORT, TRACE
__version__ = '0.5.0 alpha'
class BotChat(EventHandler, XMPPFeatureHandler):
trytimes = 0
def __init__(self):
my_jid = JID(USER+'/Bot')
self.my_jid = my_jid
settings = XMPPSettings({
"software_name": "Clubot",
"software_version": __version__,
"software_os": "Linux",
"tls_verify_peer": False,
"starttls": True,
"ipv6":False,
"poll_interval": 10,
})
settings["password"] = PASSWORD
version_provider = VersionProvider(settings)
self.connected = False
mainloop = TornadoMainLoop(settings)
self.client = Client(my_jid, [self, version_provider], settings, mainloop)
#self.client = Client(my_jid, [self, version_provider], settings)
self.logger = get_logger()
self.trytimes = 0
self.sended = []
Logics.empty_status()
def run(self, timeout = None):
self.client.connect()
self.client.run(timeout)
def disconnect(self):
self.client.disconnect()
@presence_stanza_handler("subscribe")
def handle_presence_subscribe(self, stanza):
self.logger.info(u"{0} join us".format(stanza.from_jid))
frm = stanza.from_jid
presence = Presence(to_jid = frm, stanza_type = "subscribe")
Logics.add(frm, None, stanza.show)
r =[stanza.make_accept_response(), presence]
if frm not in self.sended:
self.message_bus.send_sys_msg(stanza, new_member(frm))
self.message_bus.send_back_msg(stanza, welcome(frm))
self.sended.append(frm)
return r
@presence_stanza_handler("subscribed")
def handle_presence_subscribed(self, stanza):
self.logger.info(u"{0!r} accepted our subscription request"
.format(stanza.from_jid))
frm = stanza.from_jid
presence = Presence(to_jid = frm, stanza_type = "subscribe")
Logics.add(frm, None, stanza.show)
r =[presence]
r =[stanza.make_accept_response(), presence]
if frm not in self.sended:
self.message_bus.send_sys_msg(stanza, new_member(frm))
self.message_bus.send_back_msg(stanza, welcome(frm))
self.sended.append(frm)
return r
@presence_stanza_handler("unsubscribe")
def handle_presence_unsubscribe(self, stanza):
self.logger.info(u"{0} canceled presence subscription"
.format(stanza.from_jid))
presence = Presence(to_jid = stanza.from_jid.bare(),
stanza_type = "unsubscribe")
nick = Logics.get_one(stanza.from_jid).nick
self.message_bus.send_sys_msg(stanza, u'{0} 离开群'.format(nick))
Logics.drop(stanza.from_jid.bare())
r =[stanza.make_accept_response(), presence]
return r
@presence_stanza_handler("unsubscribed")
def handle_presence_unsubscribed(self, stanza):
self.logger.info(u"{0!r} acknowledged our subscrption cancelation"
.format(stanza.from_jid))
Logics.drop(stanza.from_jid.bare())
return True
@presence_stanza_handler(None)
def handle_presence_available(self, stanza):
self.logger.info(r"{0} has been online".format(stanza.from_jid))
if stanza.from_jid.bare().as_string() != USER:
Logics.set_online(stanza.from_jid, stanza.show)
self.message_bus.send_offline_message(stanza)
@presence_stanza_handler("unavailable")
def handle_presence_unavailable(self, stanza):
self.logger.info(r"{0} has been offline".format(stanza.from_jid))
frm = stanza.from_jid
if frm.bare().as_string() == USER:
self.logger.info('bot go to offline')
self.disconnect()
Logics.set_offline(frm)
@message_stanza_handler()
def handle_message(self, stanza):
body = stanza.body
frm = stanza.from_jid.bare().as_string()
if not body: return True
self.logger.info("receive message '{0}' from {1}"
.format(body, stanza.from_jid))
if body.startswith('$') or body.startswith('-'):
self.message_bus.send_command(stanza, body)
#elif body.startswith('<') and frm == BRIDGE:
# self.message_bus.send_qq_msg(stanza, body)
else:
self.message_bus.send_all_msg(stanza, body)
return True
@event_handler(DisconnectedEvent)
def handle_disconnected(self, event):
return QUIT
@event_handler(ConnectedEvent)
def handle_connected(self, event):
self.message_bus = MessageBus(self.my_jid, self.stream)
self.connected = True
BotChat.trytimes = 0
@property
def roster(self):
return self.client.roster
@property
def stream(self):
return self.client.stream
def invite_member(self, jid):
logging.info('invite {0}'.format(jid))
p1 = Presence(from_jid = self.my_jid, to_jid = jid,
stanza_type = 'subscribe')
p = Presence(from_jid = self.my_jid, to_jid = jid,
stanza_type = 'subscribed')
self.stream.send(p)
self.stream.send(p1)
@event_handler(RosterUpdatedEvent)
def handle_roster_update(self, event):
item = event.item
@event_handler(RosterReceivedEvent)
def handle_roster_received(self, event):
dbstatus = Logics.get_global_info('status').value
if not dbstatus:
status = STATUS
else:
status = dbstatus
p = Presence(status=status)
self.client.stream.send(p)
ret = [x.jid.bare() for x in self.roster if x.subscription == 'both']
self.logger.info(' -- roster:{0}'.format(ret))
members = Logics.get_members()
members = [m.email for m in members]
[Logics.add(frm) for frm in ret if not Logics.get_one(frm)]
if IMPORT:
[self.invite_member(JID(m)) for m in members if JID(m) not in ret]
#else:
#[del_member(JID(m)) for m in members if JID(m) not in ret]
@event_handler()
def handle_all(self, event):
self.logger.info(u"-- {0}".format(event))
def main():
if not PASSWORD:
print >>sys.stderr, 'Please write the password in the settings.py'
sys.exit(2)
if TRACE:
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
for logger in ("pyxmpp2.IN", "pyxmpp2.OUT"):
logger = logging.getLogger(logger)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.propagate = False
bot = BotChat()
retry = True
try:
bot.run()
except pyxmpp2.exceptions.SASLAuthenticationFailed:
print >>sys.stderr, 'Username or Password Error!!!'
retry = False
sys.exit(-1)
except KeyboardInterrupt:
retry = False
print >>sys.stderr, "Exiting..."
sys.exit(-2)
except:
retry = True
traceback.print_exc()
finally:
bot.disconnect()
if retry:
os.execv(sys.executable, [sys.executable] + sys.argv)
if __name__ == '__main__':
main()