-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsolferino.py
296 lines (246 loc) · 10.5 KB
/
solferino.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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
#!/usr/bin/python
import sys
import argparse
import itertools
import irc.client
import Queue
import threading
import pyrs.comms
import pyrs.rpc
import pyrs.msgs
import time
from pyrs.proto import core_pb2
from pyrs.proto import peers_pb2
from pyrs.proto import system_pb2
from pyrs.proto import chat_pb2
import pyrs.test.auth
import html2text
import unicodedata
# This will load auth parameters from file 'auth.txt'
# ONLY use for tests - make the user login properly.
auth = pyrs.test.auth.Auth()
#----------------------------------------------------------------------------
#### bot nickname retroshare side
NICK_R="buzzisha"
#### bot nickname irc side
NICK_I="buzzirco"
TIMEOUT=2
#### retroshare bridged lobbies (exact name)
BRIDGECHAN="ktest"
#### number of retry for join to retroshare lobbie
RETRYN=10
#----------------------------------------------------------------------------
LOBBIES={}
timeout = 0.5
q_i2r = Queue.Queue(maxsize=0)
q_r2i = Queue.Queue(maxsize=0)
target = None
#TODO: solve problems instead of putting patches
def decode(bytes):
if len(bytes)<508: #ugly hack with html (such as sharing certificates, for example), they produce large messages that will not be sent on irc (also to avoid flooding, limit to 512 bytes)
try:
text = bytes.decode('utf-8')
except:
try:
text = bytes.decode('iso-8859-1')
except:
try:
text = bytes.decode('cp1252')
except:
text=unicodedata.normalize('NFKD', bytes).encode('ascii','ignore')
else:
text="*MSG TOO LONG!*"
return text
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument('server')
parser.add_argument('target', help="a nickname or channel")
parser.add_argument('-p', '--port', default=6667, type=int)
return parser.parse_args()
def printazza(connection):
while not q_r2i.empty():
nick,nmsg= q_r2i.get()
if len(nick) >0 and len(nmsg)>0:
decodedmsg=decode(nmsg)
chanmsg="<%s>: %s" %(nick,decodedmsg)
q_r2i.task_done()
connection.privmsg(target,' '.join(chanmsg.split('\n')))
time.sleep(timeout)
# IRC part
class IRCBotto(threading.Thread):
def on_connect(self,connection, event):
if irc.client.is_channel(target):
connection.join(target)
return
def on_join(self,connection, event):
connection.privmsg(target, "START")
def on_disconnect(self,connection, event):
raise SystemExit()
def on_msg(self,connection, e):
nick,nmsg=e.source.nick,e.arguments[0]
q_i2r.put((nick,nmsg))
def __init__(self,re):
threading.Thread.__init__(self)
global target
args = get_args()
target = args.target
self.runevent=re
self.client = irc.client.IRC()
try:
c = self.client.server().connect(args.server, args.port, NICK_R)
except irc.client.ServerConnectionError:
print(sys.exc_info()[1])
raise SystemExit(1)
c.add_global_handler("welcome", self.on_connect)
#c.add_global_handler("join", self.on_join) NAAAAA
c.add_global_handler("disconnect", self.on_disconnect)
c.add_global_handler("pubmsg", self.on_msg)
def run(self):
while self.runevent.is_set():
printazza(self.client.connections[0]) #XXX: painful hack
try:
self.client.process_once(0.2) #TODO: here too busy waiting, is necessary to find a sensible way to make a event driven architecture
except:
pass #from bad to worse
print "bona bimbi [%s]" % self.__class__.__name__
def stop(self):
self.join()
# Retroshare part
class RetrhoshareBot(threading.Thread):
def send_on_lobby(self,cmsg):
rp = chat_pb2.RequestSendMessage()
rp.msg.id.chat_type = self.chat_type
rp.msg.id.chat_id = self.lobby_id
rp.msg.msg = cmsg
msg_id = pyrs.msgs.constructMsgId(core_pb2.CORE, core_pb2.CHAT, chat_pb2.MsgId_RequestSendMessage, False)
req_id = self.rs.request(msg_id, rp)
self.requests.append(req_id)
def __init__(self,re):
threading.Thread.__init__(self)
self.parser = pyrs.msgs.RpcMsgs()
self.comms = pyrs.comms.SSHcomms(auth.user, auth.pwd, auth.host, auth.port)
self.comms.connect()
self.rs = pyrs.rpc.RsRpc(self.comms)
self.lobby_id=""
self.chat_type=chat_pb2.TYPE_LOBBY
self.requests = []
self.runevent=re
self.next_req_cycle = []
rp = chat_pb2.RequestChatLobbies()
rp.lobby_set = chat_pb2.RequestChatLobbies.LOBBYSET_ALL
msg_id = pyrs.msgs.constructMsgId(core_pb2.CORE, core_pb2.CHAT, chat_pb2.MsgId_RequestRegisterEvents, False)
self.chat_register_id = self.rs.request(msg_id, rp)
self.requests.append(self.chat_register_id)
rp = chat_pb2.RequestSetLobbyNickname()
rp.nickname = NICK_R
msg_id = pyrs.msgs.constructMsgId(core_pb2.CORE, core_pb2.CHAT, chat_pb2.MsgId_RequestSetLobbyNickname, False)
req_id = self.rs.request(msg_id, rp)
self.requests.append(req_id)
for peer_req_id in self.requests:
ans = self.rs.response(peer_req_id, timeout)
if ans:
(msg_id, msg_body) = ans
resp = self.parser.construct(msg_id, msg_body)
if not resp:
print "Unable to Parse Response"
else:
print "No Response!"
self.requests = []
self.chatevent_msg_id = pyrs.msgs.constructMsgId(core_pb2.CORE, core_pb2.CHAT, chat_pb2.MsgId_EventChatMessage, True)
#TODO: to be reviewed completely, if no-gui is already connected, he tries RETRYN times unnecessarily and does not update the lobby_id required for proper operation!!!
r=0
while True:
print "Starting new fetch cycle"
if r> RETRYN:
return
rp = chat_pb2.RequestChatLobbies()
rp.lobby_set = chat_pb2.RequestChatLobbies.LOBBYSET_ALL
msg_id = pyrs.msgs.constructMsgId(core_pb2.CORE, core_pb2.CHAT, chat_pb2.MsgId_RequestChatLobbies, False)
print "Sending Request for ChatLobbies"
chat_listing_id = self.rs.request(msg_id, rp)
self.requests.append(chat_listing_id)
self.next_req_cycle = []
for peer_req_id in self.requests:
ans = self.rs.response(peer_req_id, timeout)
if ans:
(msg_id, msg_body) = ans
resp = self.parser.construct(msg_id, msg_body)
if resp:
if (peer_req_id == chat_listing_id):
for lobby in resp.lobbies:
if lobby.lobby_state == chat_pb2.ChatLobbyInfo.LOBBYSTATE_PUBLIC:
LOBBIES[lobby.lobby_name]=lobby.lobby_id
if lobby.lobby_name == BRIDGECHAN: #when I arrived here, I found the chat lobby was looking for, is now to take the id
print ""
print "-"*20
print "Sending Request to Join Public ChatLobby %s" % (lobby.lobby_name)
self.lobby_id=lobby.lobby_id
print "-"*20
req_id = self.join_leave(lobby.lobby_id,True)
self.next_req_cycle.append(req_id)
return
else:
sys.stdout.write("[.]")
else:
sys.stdout.write("[Unable to Parse Response]")
else:
sys.stdout.write("No Response!")
continue
self.requests = self.next_req_cycle
r=r+1
def join_leave(self,lid,join):
rp = chat_pb2.RequestJoinOrLeaveLobby()
rp.lobby_id = lid
if join:
rp.action = chat_pb2.RequestJoinOrLeaveLobby.JOIN_OR_ACCEPT
else:
rp.action = chat_pb2.RequestJoinOrLeaveLobby.LEAVE_OR_DENY
msg_id = pyrs.msgs.constructMsgId(core_pb2.CORE, core_pb2.CHAT, chat_pb2.MsgId_RequestJoinOrLeaveLobby, False)
req_id = self.rs.request(msg_id, rp)
return req_id
def run(self):
while self.runevent.is_set():
while not q_i2r.empty(): #if have stuff in the queue send it!
nick,nmsg=q_i2r.get()
cmsg= "<%s>: %s"%(nick,nmsg)
self.send_on_lobby(cmsg)
q_i2r.task_done()
try:
time.sleep(timeout) ## TODO: this is the most controversial. In fact makes active waiting with timeout range
except KeyboardInterrupt: ## TODO manage somehow termination
inp = raw_input( 'prompt$ ' )
if(inp in ["Exit","exit","Quit","quit"] ):
break
if(inp in ["listlobbies"]):
print LOBBIES.keys()
ans = self.rs.response(self.chat_register_id, timeout)
if ans:
try:
(msg_id, msg_body) = ans
resp = self.parser.construct(msg_id, msg_body)
if resp.msg.id.chat_type == chat_pb2.TYPE_LOBBY and resp.msg.id.chat_id==self.lobby_id: #XXX: check if the msg is on right lobbie
msgbody=html2text.html2text(resp.msg.msg)
q_r2i.put( (resp.msg.peer_nickname,msgbody))
except:
pass
self.join_leave(self.lobby_id,False)
print "bona bimbi [%s]" % self.__class__.__name__
def stop(self):
self.comms.close()
self.join()
if __name__ == '__main__':
run_event = threading.Event()
run_event.set()
t1 = RetrhoshareBot(run_event)
t2 = IRCBotto(run_event)
t1.start()
t2.start()
try:
while 1:
time.sleep(.1)
except KeyboardInterrupt:
print "attempting to close threads. "
run_event.clear()
t1.join()
t2.join()
print "threads successfully closed"