Advertisement
Guest User

Untitled

a guest
Aug 6th, 2015
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 30.40 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. #Copyright (c) 2013-2015 Charles Ricketts <chuck.the.pc.guy@gmail.com>
  4. #
  5. # Copyright (c) 2015, Charles Ricketts <chuck.the.pc.guy@gmail.com>
  6. # All rights reserved.
  7. #
  8. # Redistribution and use in source and binary forms, with or without
  9. # modification, are permitted provided that the following conditions are met:
  10. # * Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # * Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in the
  14. # documentation and/or other materials provided with the distribution.
  15. # * Neither the name of AFKBot nor the
  16. # names of its contributors may be used to endorse or promote products
  17. # derived from this software without specific prior written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  20. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. # DISCLAIMED. IN NO EVENT SHALL AFKBOT'S CONTRIBUTORS BE LIABLE FOR ANY
  23. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29.  
  30. #Contains code from mumblebee
  31. #Copyright (c) 2013, Toshiaki Hatano <haeena@haeena.net>
  32. #
  33. #Contains code from the eve-bot
  34. #Copyright (c) 2009, Philip Cass <frymaster@127001.org>
  35. #Copyright (c) 2009, Alan Ainsworth <fruitbat@127001.org>
  36. #
  37. #Contains code from the Mumble Project:
  38. #Copyright (C) 2005-2009, Thorvald Natvig <thorvald@natvig.com>
  39. #
  40. #All rights reserved.
  41. #
  42. #Redistribution and use in source and binary forms, with or without
  43. #modification, are permitted provided that the following conditions
  44. #are met:
  45. #
  46. #- Redistributions of source code must retain the above copyright notice,
  47. # this list of conditions and the following disclaimer.
  48. #- Redistributions in binary form must reproduce the above copyright notice,
  49. # this list of conditions and the following disclaimer in the documentation
  50. # and/or other materials provided with the distribution.
  51. #- Neither the name of localhost, 127001.org, eve-bot nor the names of its
  52. # contributors may be used to endorse or promote products derived from this
  53. # software without specific prior written permission.
  54.  
  55. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  56. # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  57. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  58. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
  59. # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  60. # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  61. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  62. # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  63. # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  64. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  65. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  66.  
  67. import socket
  68. import time
  69. import struct
  70. import sys
  71. import select
  72. import thread
  73. import threading
  74. import signal
  75. import os
  76. import optparse
  77. import platform
  78. import random
  79. #import daemon
  80. import ConfigParser
  81.  
  82. import pdb
  83. from pprint import pprint
  84. #The next 2 imports may not succeed
  85. warning=""
  86. try:
  87. import ssl
  88. except:
  89. warning+="WARNING: This python program requires the python ssl module (available in python 2.6; standalone version may be at found http://pypi.python.org/pypi/ssl/)\n"
  90. try:
  91. import Mumble_pb2
  92. except:
  93. warning+="WARNING: Module Mumble_pb2 not found\n"
  94. warning+="This program requires the Google Protobuffers library (http://code.google.com/apis/protocolbuffers/) to be installed\n"
  95. warning+="You must run the protobuf compiler \"protoc\" on the Mumble.proto file to generate the Mumble_pb2 file\n"
  96. warning+="Move the Mumble.proto file from the mumble source code into the same directory as this bot and type \"protoc --python_out=. Mumble.proto\"\n"
  97.  
  98. headerFormat=">HI"
  99. eavesdropper=None
  100. messageLookupMessage={Mumble_pb2.Version:0,Mumble_pb2.UDPTunnel:1,Mumble_pb2.Authenticate:2,Mumble_pb2.Ping:3,Mumble_pb2.Reject:4,Mumble_pb2.ServerSync:5,
  101. Mumble_pb2.ChannelRemove:6,Mumble_pb2.ChannelState:7,Mumble_pb2.UserRemove:8,Mumble_pb2.UserState:9,Mumble_pb2.BanList:10,Mumble_pb2.TextMessage:11,Mumble_pb2.PermissionDenied:12,
  102. Mumble_pb2.ACL:13,Mumble_pb2.QueryUsers:14,Mumble_pb2.CryptSetup:15,Mumble_pb2.ContextActionModify:16,Mumble_pb2.ContextAction:17,Mumble_pb2.UserList:18,Mumble_pb2.VoiceTarget:19,
  103. Mumble_pb2.PermissionQuery:20,Mumble_pb2.CodecVersion:21,Mumble_pb2.UserStats:22,Mumble_pb2.RequestBlob:23,Mumble_pb2.ServerConfig:24,Mumble_pb2.SuggestConfig:25}
  104. messageLookupNumber={}
  105. threadNumber=0
  106.  
  107. for i in messageLookupMessage.keys():
  108. messageLookupNumber[messageLookupMessage[i]]=i
  109.  
  110. class Logger(object):
  111. def __init__(self, filename="Default.log"):
  112. self.terminal = sys.stdout
  113. self.log = open(filename, "a")
  114.  
  115. def write(self, message):
  116. self.terminal.write(message)
  117. self.log.write(message)
  118.  
  119. sys.stdout = Logger("mumblebot.log")
  120. sys.stderr = sys.stdout
  121.  
  122. def discontinue_processing(signl, frme):
  123. global eavesdropper
  124. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"), "Received shutdown notice"
  125. if signl == signal.SIGUSR1:
  126. pbMess = Mumble_pb2.TextMessage()
  127. pbMess.actor = eavesdropper.session
  128. for channel_id in eavesdropper.channelList:
  129. pbMess.channel_id.append(channel_id)
  130. pbMess.message = "Server rebooting!"
  131. eavesdropper.sendTotally(eavesdropper.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString()))
  132. if eavesdropper:
  133. eavesdropper.wrapUpThread()
  134. else:
  135. sys.exit(0)
  136.  
  137. signal.signal( signal.SIGINT, discontinue_processing )
  138. #signal.signal( signal.SIGQUIT, discontinue_processing )
  139. signal.signal( signal.SIGTERM, discontinue_processing )
  140. signal.signal( signal.SIGUSR1, discontinue_processing )
  141.  
  142. class timedWatcher(threading.Thread):
  143. def __init__(self,socketLock,socket):
  144. global threadNumber
  145. threading.Thread.__init__(self)
  146. self.pingTotal=1
  147. self.isRunning=True
  148. self.socketLock=socketLock
  149. self.socket=socket
  150. i = threadNumber
  151. threadNumber+=1
  152. self.threadName="Thread " + str(i)
  153.  
  154. def stopRunning(self):
  155. self.isRunning=False
  156.  
  157. def run(self):
  158. self.nextPing=time.time()-1
  159.  
  160. while self.isRunning:
  161. t=time.time()
  162. if t>self.nextPing:
  163. pbMess = Mumble_pb2.Ping()
  164. pbMess.timestamp=(self.pingTotal*5000000)
  165. pbMess.good=0
  166. pbMess.late=0
  167. pbMess.lost=0
  168. pbMess.resync=0
  169. pbMess.udp_packets=0
  170. pbMess.tcp_packets=self.pingTotal
  171. pbMess.udp_ping_avg=0
  172. pbMess.udp_ping_var=0.0
  173. pbMess.tcp_ping_avg=50
  174. pbMess.tcp_ping_var=50
  175. self.pingTotal+=1
  176. packet=struct.pack(headerFormat,3,pbMess.ByteSize())+pbMess.SerializeToString()
  177. self.socketLock.acquire()
  178. while len(packet)>0:
  179. sent=self.socket.send(packet)
  180. packet = packet[sent:]
  181. self.socketLock.release()
  182. self.nextPing=t+10
  183. #sleeptime=self.nextPing-t
  184. #if sleeptime > 0:
  185. time.sleep(0.5)
  186. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"timed thread going away"
  187.  
  188. class mumbleConnection(threading.Thread):
  189. def __init__(self,host=None,nickname=None,channel=None,delay=None,limit=None,password=None,verbose=False,certificate=None,idletime=30):
  190. global threadNumber
  191. i = threadNumber
  192. threadNumber+=1
  193. self.threadName="Thread " + str(i)
  194. threading.Thread.__init__(self)
  195. tcpSock=socket.socket(type=socket.SOCK_STREAM)
  196. self.socketLock=thread.allocate_lock()
  197. self.socket=ssl.wrap_socket(tcpSock,certfile=certificate,ssl_version=ssl.PROTOCOL_TLSv1)
  198. self.socket.setsockopt(socket.SOL_TCP,socket.TCP_NODELAY,1)
  199. self.host=host
  200. self.nickname=nickname
  201. self.inChannel=False
  202. self.session=None
  203. self.channelId=None
  204. self.userList={}
  205. self.userListByName={}
  206. self.channelList={}
  207. self.channelListByName={}
  208. self.readyToClose=False
  209. self.timedWatcher = None
  210. # TODO: Implement delay and rate limit
  211. self.delay=delay
  212. self.limit=limit
  213. self.password=password
  214. self.verbose=verbose
  215.  
  216. #######################################
  217. # AFKBot-specific config
  218. #######################################
  219. self.idleLimit=idletime #Idle limit in minutes
  220. self.channel=channel #AFK channel to listen in
  221. def decodePDSInt(self,m,si=0):
  222. v = ord(m[si])
  223. if ((v & 0x80) == 0x00):
  224. return ((v & 0x7F),1)
  225. elif ((v & 0xC0) == 0x80):
  226. return ((v & 0x4F) << 8 | ord(m[si+1]),2)
  227. elif ((v & 0xF0) == 0xF0):
  228. if ((v & 0xFC) == 0xF0):
  229. return (ord(m[si+1]) << 24 | ord(m[si+2]) << 16 | ord(m[si+3]) << 8 | ord(m[si+4]),5)
  230. elif ((v & 0xFC) == 0xF4):
  231. return (ord(m[si+1]) << 56 | ord(m[si+2]) << 48 | ord(m[si+3]) << 40 | ord(m[si+4]) << 32 | ord(m[si+5]) << 24 | ord(m[si+6]) << 16 | ord(m[si+7]) << 8 | ord(m[si+8]),9)
  232. elif ((v & 0xFC) == 0xF8):
  233. result,length=decodePDSInt(m,si+1)
  234. return(-result,length+1)
  235. elif ((v & 0xFC) == 0xFC):
  236. return (-(v & 0x03),1)
  237. else:
  238. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),"Help Help, out of cheese :("
  239. sys.exit(1)
  240. elif ((v & 0xF0) == 0xE0):
  241. return ((v & 0x0F) << 24 | ord(m[si+1]) << 16 | ord(m[si+2]) << 8 | ord(m[si+3]),4)
  242. elif ((v & 0xE0) == 0xC0):
  243. return ((v & 0x1F) << 16 | ord(m[si+1]) << 8 | ord(m[si+2]),3)
  244. else:
  245. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),"out of cheese?"
  246. sys.exit(1)
  247.  
  248. def packageMessageForSending(self,msgType,stringMessage):
  249. length=len(stringMessage)
  250. return struct.pack(headerFormat,msgType,length)+stringMessage
  251.  
  252. def sendTotally(self,message):
  253. self.socketLock.acquire()
  254. while len(message)>0:
  255. sent=self.socket.send(message)
  256. if sent < 0:
  257. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"Server socket error while trying to write, immediate abort"
  258. self.socketLock.release()
  259. return False
  260. message=message[sent:]
  261. self.socketLock.release()
  262. return True
  263.  
  264. def readTotally(self,size):
  265. message=""
  266. while len(message)<size:
  267. received=self.socket.recv(size-len(message))
  268. message+=received
  269. if len(received)==0:
  270. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"Server socket died while trying to read, immediate abort"
  271. return None
  272. return message
  273.  
  274. def parseMessage(self,msgType,stringMessage):
  275. msgClass=messageLookupNumber[msgType]
  276. message=msgClass()
  277. message.ParseFromString(stringMessage)
  278. return message
  279.  
  280. def joinChannel(self):
  281. if self.channelId!=None and self.session!=None:
  282. pbMess = Mumble_pb2.UserState()
  283. pbMess.session=self.session
  284. pbMess.channel_id=self.channelId
  285. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  286. self.wrapUpThread()
  287. return
  288. self.inChannel=True
  289.  
  290. def wrapUpThread(self):
  291. #called after thread is confirmed to be needing to die because of kick / socket close
  292. self.readyToClose=True
  293.  
  294. def readPacket(self):
  295. #pdb.set_trace()
  296. meta=self.readTotally(6)
  297. if not meta:
  298. self.wrapUpThread()
  299. return
  300. msgType,length=struct.unpack(headerFormat,meta)
  301. stringMessage=self.readTotally(length)
  302. if not stringMessage:
  303. self.wrapUpThread()
  304. return
  305. #Type 1 = UDP Tunnel, voice data
  306. if msgType==1:
  307. session,sessLen=self.decodePDSInt(stringMessage,1)
  308. if session in self.userList and self.userList[session]["channel"] == self.channelListByName[self.channel]:
  309. if "idlesecs" in self.userList[session] and "oldchannel" in self.userList[session]["idlesecs"]:
  310. pbMess = Mumble_pb2.UserState()
  311. pbMess.session = session
  312. pbMess.actor = self.session
  313. pbMess.channel_id = self.userList[session]["idlesecs"]["oldchannel"]
  314. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  315. self.wrapUpThread()
  316. #Type 5 = ServerSync
  317. if (not self.session) and msgType==5 and (not self.inChannel):
  318. message=self.parseMessage(msgType,stringMessage)
  319. self.session=message.session
  320. self.joinChannel()
  321. #Type 6 = ChannelRemove
  322. if msgType == 6:
  323. message=self.parseMessage(msgType,stringMessage)
  324. channelid=message.channel_id
  325. for item in self.channelListByName:
  326. if self.channelListByName[item] == message.channel_id:
  327. del self.channelListByName[item]
  328. break
  329. for item in self.userList:
  330. if "idlesecs" in self.userList[item] and "oldchannel" in self.userList[item]["idlesecs"]:
  331. if self.userList[item]["idlesecs"]["oldchannel"] == channelid:
  332. self.userList[item]["idlesecs"]["oldchannel"] = 0;
  333. #Type 7 = ChannelState
  334. if msgType == 7: #(not self.inChannel) and msgType==7 and self.channelId==None:
  335. message=self.parseMessage(msgType,stringMessage)
  336. if message.channel_id not in self.channelList or self.channelList[message.channel_id] != message.name:
  337. self.channelList[message.channel_id]=message.name
  338. self.channelListByName[message.name]=message.channel_id
  339. if (not self.inChannel) and self.channelId==None:
  340. if self.channel==None and message.channel_id==0:
  341. self.channel=message.name
  342. self.channelId=message.channel_id
  343. self.joinChannel()
  344. if message.name==self.channel:
  345. self.channelId=message.channel_id
  346. self.joinChannel()
  347. #Type 8 = UserRemove (kick)
  348. if msgType==8:
  349. message=self.parseMessage(msgType,stringMessage)
  350. if self.session!=None:
  351. if message.session==self.session:
  352. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"********* KICKED ***********"
  353. self.wrapUpThread()
  354. return
  355. session=message.session
  356. if session in self.userList:
  357. temp = self.userList[session]
  358. del self.userListByName[self.userList[session]["name"]]
  359. del self.userList[session]
  360. #Type 9 = UserState
  361. if msgType==9:
  362. message=self.parseMessage(msgType,stringMessage)
  363. session=message.session
  364. if session in self.userList:
  365. record=self.userList[session]
  366. else:
  367. record={"session":session}
  368. self.userList[session]=record
  369. name=None
  370. channel=None
  371. if message.HasField("name"):
  372. name=message.name
  373. if "name" in record and record["name"] in self.userListByName:
  374. del self.userListByName[record["name"]]
  375. record["name"]=name
  376. self.userListByName[name]=message.session
  377. if message.HasField("channel_id"):
  378. channel=message.channel_id
  379. record["channel"]=channel
  380. if name and not channel:
  381. record["channel"]=0
  382. channelName = self.channelList[record["channel"]]
  383. #If they're not already in the AFK channel
  384. if message.channel_id != self.channelListByName[self.channel]:
  385. #Send a query for UserStats -- needed to get idletime
  386. if "idlesecs" in record:
  387. record["idlesecs"]["checksent"] = True
  388. record["idlesecs"]["oldchannel"] = message.channel_id
  389. else:
  390. record["idlesecs"]={"checksent":True,"oldchannel":message.channel_id}
  391. if message.HasField("actor"): # and message.actor != self.session:
  392. record["idlesecs"]["checksent"] = False
  393. record["idlesecs"]["checkon"] = time.time()+self.idleLimit*60
  394. else:
  395. pbMess = Mumble_pb2.UserStats()
  396. pbMess.session = session
  397. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  398. self.wrapUpThread()
  399. else:
  400. if "idlesecs" in record:
  401. record["idlesecs"]["checksent"] = False
  402. record["idlesecs"]["checkon"] = -1
  403. # if "oldchannel" not in record["idlesecs"]:
  404. ## record["idlesecs"]["oldchannel"] = 0
  405. # else:
  406. # record["idlesecs"]["oldchannel"] = message.channel_id
  407. else:
  408. record["idlesecs"]={"checksent":False,"checkon":-1,"oldchannel":0}
  409. if self.inChannel and channelName == "Private Chats":
  410. pbMess = Mumble_pb2.TextMessage();
  411. pbMess.actor = self.session;
  412. pbMess.session.append(message.session);
  413. pbMess.message = "This is the Private Chats channel. To create a sub-channel, right click Private Chats and select 'Add'. Name your channel and check the 'Temporary' checkbox.";
  414. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  415. self.wrapUpThread()
  416. if self.inChannel and channelName in ("Castle Wars","Zamorak","Saradomin"):
  417. #Package a message to the user
  418. pbMess = Mumble_pb2.TextMessage();
  419. pbMess.actor = self.session;
  420. pbMess.session.append(message.session);
  421. pbMess.message = "Welcome to the Castle Wars channels! In this channel setup you can set up a hotkey for Shout with a target of the parent channel to send messages to the opposing team.";
  422. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  423. self.wrapUpThread()
  424. return
  425.  
  426. #Type 11 = TextMessage
  427. if msgType==11:
  428. message=self.parseMessage(msgType,stringMessage)
  429. if message.actor!=self.session:
  430. if message.message.startswith("/roll"):
  431. pbMess = Mumble_pb2.TextMessage()
  432. pbMess.actor = self.session
  433. pbMess.channel_id.append(self.channelId)
  434. pbMess.channel_id.append(self.userList[message.actor]["channel"])
  435. pbMess.message = self.userList[message.actor]["name"] + " rolled " + str(random.randint(0,100))
  436. pbMess.session.append(self.session)
  437. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  438. self.wrapUpThread()
  439. return
  440. if message.message.startswith("/afkme"):
  441. pbMess = Mumble_pb2.UserState()
  442. pbMess.session = message.actor
  443. pbMess.actor = self.session
  444. pbMess.channel_id = self.channelListByName[self.channel]
  445. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  446. self.wrapUpThread()
  447. self.userList[message.actor]["idlesecs"]["checkon"] = -1
  448. return
  449. if message.message.startswith("/afk"):
  450. args = message.message.split(" ",1)
  451. pbMess = Mumble_pb2.UserState()
  452. for key in self.userListByName:
  453. if key.lower() == args[1].lower():
  454. pbMess.session = self.userListByName[key]
  455. print pbMess.session;
  456. if pbMess.session == 0:
  457. pbMess = Mumble_pb2.TextMessage();
  458. pbMess.actor = self.session;
  459. pbMess.session.append(message.actor);
  460. pbMess.message = "No such user to AFK";
  461. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  462. self.wrapUpThread()
  463. return
  464. pbMess.actor = self.session
  465. pbMess.channel_id = self.channelListByName[self.channel]
  466. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  467. self.wrapUpThread()
  468. self.userList[pbMess.session]["idlesecs"]["checkon"] = -1
  469. return
  470. if message.message.startswith("/unafk"):
  471. args = message.message.split(" ",1)
  472. pbMess = Mumble_pb2.UserState()
  473. for key in self.userListByName:
  474. if key.lower() == args[1].lower():
  475. pbMess.session = self.userListByName[key]
  476. pbMess.channel_id = self.userList[pbMess.session]["idlesecs"]["oldchannel"]
  477. if pbMess.session == 0:
  478. pbMess = Mumble_pb2.TextMessage();
  479. pbMess.actor = self.session;
  480. pbMess.session.append(message.actor);
  481. pbMess.message = "No such user to UnAFK";
  482. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  483. self.wrapUpThread()
  484. return
  485. pbMess.actor = self.session
  486. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  487. self.wrapUpThread()
  488. self.userList[pbMess.session]["idlesecs"]["checkon"] = -1
  489.  
  490. #Type 12 = PermissionDenied
  491. if msgType==12:
  492. print "Permission Denied."
  493. return
  494.  
  495. #Type 22 = UserStats
  496. if msgType==22:
  497. message=self.parseMessage(msgType,stringMessage)
  498. self.userList[message.session]["idlesecs"]["idlesecs"] = message.idlesecs
  499. self.userList[message.session]["idlesecs"]["checkon"] = time.time()+((self.idleLimit*60)-message.idlesecs)
  500. if message.idlesecs > self.idleLimit*60 and message.session != self.session:
  501. #self.userList[message.session]["idlesecs"]["oldchannel"] = self.userList[message.session]["channel"]
  502. #Move user to AFK channel
  503. pbMess = Mumble_pb2.UserState()
  504. pbMess.session = message.session
  505. pbMess.actor = self.session
  506. pbMess.channel_id = self.channelListByName[self.channel];
  507. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  508. self.wrapUpThread()
  509. self.userList[message.session]["idlesecs"]["checkon"] = -1
  510. self.userList[message.session]["idlesecs"]["checksent"] = False
  511. return
  512.  
  513.  
  514. def run(self):
  515. try:
  516. self.socket.connect(self.host)
  517. except Exception as inst:
  518. print type(inst)
  519. print inst
  520. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"Couldn't connect to server"
  521. return
  522. self.socket.setblocking(False)
  523. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"connected to server"
  524. pbMess = Mumble_pb2.Version()
  525. pbMess.release="1.2.8"
  526. pbMess.version=66052
  527. pbMess.os=platform.system()
  528. pbMess.os_version="AFKBot0.5.0"
  529.  
  530. initialConnect=self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())
  531.  
  532. pbMess = Mumble_pb2.Authenticate()
  533. pbMess.username=self.nickname
  534. if self.password!=None:
  535. pbMess.password=self.password
  536. celtversion=pbMess.celt_versions.append(-2147483637)
  537.  
  538. initialConnect+=self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())
  539.  
  540. if not self.sendTotally(initialConnect):
  541. return
  542.  
  543. sockFD=self.socket.fileno()
  544.  
  545. self.timedWatcher = timedWatcher(self.socketLock,self.socket)
  546. self.timedWatcher.start()
  547. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"started timed watcher",self.timedWatcher.threadName
  548.  
  549. while True:
  550. pollList,foo,errList=select.select([sockFD],[],[sockFD],5)
  551. for item in pollList:
  552. if item==sockFD:
  553. try:
  554. self.readPacket()
  555. except ssl.SSLError:
  556. continue
  557. for session in self.userList:
  558. record = self.userList[session]
  559. if "idlesecs" in record:
  560. if record["name"] != self.nickname and record["idlesecs"]["checksent"] == False and record["idlesecs"]["checkon"] > -1 and record["idlesecs"]["checkon"] < time.time():
  561. pbMess = Mumble_pb2.UserStats();
  562. pbMess.session = session
  563. if not self.sendTotally(self.packageMessageForSending(messageLookupMessage[type(pbMess)],pbMess.SerializeToString())):
  564. self.wrapUpThread()
  565. record["idlesecs"]["checksent"] = True
  566. if self.readyToClose:
  567. self.wrapUpThread()
  568. break
  569.  
  570. if self.timedWatcher:
  571. self.timedWatcher.stopRunning()
  572.  
  573. self.socket.close()
  574. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"waiting for timed watcher to die..."
  575. if self.timedWatcher!=None:
  576. while self.timedWatcher.isAlive():
  577. pass
  578. print time.strftime("%a, %d %b %Y %H:%M:%S +0000"),self.threadName,"thread going away -",self.nickname
  579.  
  580. def main():
  581. global eavesdropper,warning
  582.  
  583. p = optparse.OptionParser(description='Mumble 1.2 AFKBot',
  584. prog='afkbot.py',
  585. version='%prog 0.5.0',
  586. usage='\t%prog')
  587.  
  588. p.add_option("-a","--afk-channel",help="Channel to eavesdrop in (default %%Root)",action="store",type="string",default="AFK")
  589. p.add_option("-s","--server",help="Host to connect to (default %default)",action="store",type="string",default="localhost")
  590. p.add_option("-p","--port",help="Port to connect to (default %default)",action="store",type="int",default=64738)
  591. p.add_option("-n","--nick",help="Nickname for the AFKBot (default %default)",action="store",type="string",default="AFKBot")
  592. p.add_option("-d","--delay",help="Time to delay response by in seconds (default %default)",action="store",type="float",default=0)
  593. p.add_option("-l","--limit",help="Maximum response per minutes (default %default, 0 = unlimited)",action="store",type="int",default=0)
  594. # p.add_option("-c","--config",help="Configuration file",action="store",type="string",default="mumblebee.cfg")
  595. p.add_option("-c","--certificate",help="Certificate file for the bot to use when connecting to the server (.pem)",action="store",type="string",default="afkbot.pem")
  596. p.add_option("-i","--idle-time",help="Time (in minutes) before user is moved to the AFK channel",action="store",type="int",default=30);
  597. p.add_option("-v","--verbose",help="Outputs and translates all messages received from the server",action="store_true",default=False)
  598. p.add_option("--password",help="Password for server, if any",action="store",type="string")
  599.  
  600. if len(warning)>0:
  601. print warning
  602. o, arguments = p.parse_args()
  603. if len(warning)>0:
  604. sys.exit(1)
  605.  
  606. host=(o.server,o.port)
  607.  
  608. #sys.stdout = open("mumblebot.log","w")
  609. #sys.stderr = sys.stdout
  610.  
  611. #daemoninstance = daemon.DaemonContext()
  612. #daemoninstance.stdout = logfile;
  613. #daemoninstance.1
  614.  
  615. eavesdropper = mumbleConnection(host,o.nick,o.afk_channel,delay=o.delay,limit=o.limit,password=o.password,verbose=o.verbose,certificate=o.certificate,idletime=o.idle_time)
  616. eavesdropper.start()
  617.  
  618. #Need to keep main thread alive to receive shutdown signal
  619.  
  620. while eavesdropper.isAlive():
  621. time.sleep(0.5)
  622.  
  623. return 0
  624.  
  625. if __name__ == '__main__':
  626. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement