bot.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #!/usr/bin/env python3
  2. import asyncore
  3. import threading
  4. import time
  5. import re
  6. import socket
  7. import sys
  8. from unzipper import unar
  9. class IRCClient(asyncore.dispatcher):
  10. HOST="irc.irchighway.net"
  11. PORT=6667
  12. NICK="booksbot3321321"
  13. IDENT="booksbot"
  14. REALNAME="NotABot"
  15. IGNORE=["NOTICE","PART","QUIT", "332","333", "372", "353","366", "251", "252", "254", "255","265","266","396"]
  16. LOOKING_FOR="cryptonomicon"
  17. CHANNEL="#ebooks"
  18. EXTENSION="epub"
  19. buffer = []
  20. readbuffer=b''
  21. joined=False
  22. connected=False
  23. def __init__(self,book,extension):
  24. asyncore.dispatcher.__init__(self)
  25. self.EXTENSION=extension
  26. self.LOOKING_FOR=book
  27. self.create_socket()
  28. self.connect( (self.HOST, self.PORT) )
  29. nickstr="NICK %s\r\n" % self.NICK
  30. userstr="USER %s %s bla :%s\r\n" % (self.IDENT, self.HOST, self.REALNAME)
  31. self.send_queue(nickstr)
  32. self.send_queue(userstr)
  33. def handle_connect(self):
  34. print("connected")
  35. self.connected=True
  36. pass
  37. def handle_close(self):
  38. print("closed")
  39. self.connected=False
  40. self.close()
  41. def handle_read(self):
  42. data=self.recv(1024)
  43. if not (data[-1]==10 and data[-2]==13):
  44. self.readbuffer+=data
  45. return
  46. data=self.readbuffer+data
  47. data=data.replace(b'\x95', b'').replace(b'0xc2',b'').decode('utf-8','ignore') #purge mojibake
  48. self.readbuffer=b''
  49. lines=data.splitlines()
  50. for line in lines:
  51. words=line.split(' ')
  52. if len(words)<2:
  53. continue
  54. msg_from=words[0]
  55. comm=words[1]
  56. if comm in self.IGNORE:
  57. continue
  58. if comm=="JOIN":
  59. if self.NICK not in msg_from:
  60. continue
  61. print("Joined channel %s" % self.CHANNEL)
  62. self.search(self.LOOKING_FOR)
  63. self.joined=True
  64. if comm=="PRIVMSG":
  65. if words[2] != self.NICK:
  66. continue
  67. self.parse_msg(line);
  68. if comm=="PING" or msg_from=="PING":
  69. self.pong(line)
  70. continue
  71. if comm=="376": #END MOTD
  72. self.join(self.CHANNEL)
  73. continue
  74. if not self.joined:
  75. continue
  76. def parse_msg(self,msg):
  77. msg=msg.split(':')[2]
  78. msg=msg.replace("\x01","")
  79. args=msg.split(" ")
  80. size=int(args.pop())
  81. port=int(args.pop())
  82. ip=self.ip_from_decimal(int(args.pop()))
  83. args.pop(0) #"DCC"
  84. args.pop(0) #"SEND"
  85. filename="_".join(args)
  86. fname='/tmp/1234-%s' % filename
  87. n=self.netcat(ip,port,size,fname)
  88. files=unar(fname,"/tmp/")
  89. for f in files:
  90. if "searchbot" in f.lower():
  91. self.list_books(f)
  92. def list_books(self,f):
  93. f=open(f,"r")
  94. lines=f.readlines()
  95. lines=[l.strip() for l in lines if self.EXTENSION in l.lower() ]
  96. for l in lines:
  97. print(l)
  98. return lines
  99. def search(self,book):
  100. print("searching for %s" % book)
  101. self.send_queue("PRIVMSG %s :@search %s \r\n" % (self.CHANNEL,book))
  102. def pong(self,data):
  103. msg=data.replace("PING ","")
  104. self.send_queue("PONG %s\r\n" % msg)
  105. def join(self,channel):
  106. self.send_queue("JOIN %s\r\n" % channel)
  107. def writable(self):
  108. return (len(self.buffer) > 0 and self.connected)
  109. def handle_write(self):
  110. out=self.buffer.pop()
  111. self.send(out)
  112. def send_queue(self,msg):
  113. add=bytes(str(msg),"utf-8")
  114. self.buffer.append(add)
  115. def book(self,book):
  116. print("asking for %s" % book)
  117. self.send_queue("PRIVMSG %s :%s \r\n" % (self.CHANNEL,book))
  118. def who(self):
  119. self.send_queue("WHO %s" % self.CHANNEL)
  120. def ip_from_decimal(self,dec):
  121. return ".".join([ str(int(b,2)) for b in self.b_octets(bin(dec)[2:]) ])
  122. def b_octets(self,l):
  123. l=l.zfill(32)
  124. return [l[0:8],l[8:16],l[16:24],l[24:32]]
  125. def netcat(self,ip,port,size,fname):
  126. s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  127. s.connect((ip,port))
  128. #s.shutdown(socket.SHUT_WR)
  129. sizelen=len(str(size))
  130. f=open(fname, 'wb')
  131. count=0
  132. while True:
  133. data = s.recv(1024)
  134. if len(data)==0:
  135. print("empty")
  136. break
  137. count+=len(data)
  138. f.write(data)
  139. #sys.stdout.write("\r%s/%d" % (str(count).zfill(sizelen),size))
  140. #progress
  141. if count >= size:
  142. break
  143. s.close()
  144. f.close()
  145. if len(sys.argv)!=3:
  146. print("USAGE: %s <BOOK> <FORMAT>" % sys.argv[0])
  147. sys.exit(1)
  148. print("Looking for %s in format %s" % (sys.argv[1],sys.argv[2]))
  149. client = IRCClient(sys.argv[1],sys.argv[2])
  150. loop_thread = threading.Thread(target=asyncore.loop, name="Asyncore Loop")
  151. loop_thread.start()