ICQ бот на Python

Привет %habrauser%! Это мой первый хабротопик. В котором я расскажу как можно сделать ICQ бот на pythone. Бот у нас будет сидеть в сети и отправлять, по запросу пользователя, список последних статей хабра.
Для реализации бота мы будем использовать библиотеку Twisted. Конечно же есть уже специализированные библиотеки для работы с ICQ на python. Такие как py-icq или nanoicq. Но так как с Twisted я был уже знаком, то выбор пал именно на него.

Дабы не изобретать велосипед, возьмем львиную часть программы из примера:

  1. # UIN
  2. UIN = ""    #Наш уин
  3. PASS = "" #Соответственно пароль
  4. host = ("login.icq.com", 5238) # Сервер/порт
  5. icqMode = 1
  6. # Status message
  7. AMSG = "I'm here +)"
* This source code was highlighted with Source Code Highlighter.


Тут начальная часть у нас и заканчивается. Загрузим необходимые библиотеки.

  1. from twisted.words.protocols import oscar
  2. from twisted.internet import protocol, reactor
  3. import feedparser
  4. import urllib2  #Прошу не забивать голову пока этим. Для чего это, расскажу чуть позже
* This source code was highlighted with Source Code Highlighter.


Feedparser нам нужен для того чтобы стянуть и отпарсить rss ленту хабры. Теперь сам бот. Тут почти все от примера, за исключением мелких изменений…

  1. # Class Bot
  2. class Bot(oscar.BOSConnection):
  3.     capabilities = [oscar.CAP_CHAT]
  4.     def initDone(self):
  5.         print "Connect ",UIN," to server", host[0], host[1]     
  6.         self.requestSelfInfo().addCallback(self.gotSelfInfo)
  7.         self.requestSSI().addCallback(self.gotBuddyList)
  8.         self.setAway(AMSG)
  9.     def gotSelfInfo(self, user):
  10.         print user.__dict__
  11.         self.name = user.name
  12.     def gotBuddyList(self, l):
  13.         print l
  14.         self.activateSSI()
  15.         self.setProfile("""ICQBot""")
  16.         self.setIdleTime(0)
  17.         self.clientReady()
  18.     def gotAway(self, away, user):
  19.         if away:
  20.             print "User ", user,": ",away
* This source code was highlighted with Source Code Highlighter.


Тут-то у нас самая вкусная часть. Обработка входящих сообщений:

  1. def receiveMessage(self, user, multiparts, flags):
  2.         print "\n< From: ", user.name
  3.         print "< Message: ", multiparts[0][0].decode('cp1251')
  4.         command = multiparts[0][0].lower().split(' ')             #разбиваем сообщение по пробелам
  5.         PREF_C = "!"                                                            #команды у нас будут начинаться с !
* This source code was highlighted with Source Code Highlighter.


Первая команда у нас будет !habr, но тут есть небольшие исключения. Если команда синтаксиса !habr n, то будет выведено последние n постов. Если же команда синтаксиса !habr n1 n2, то с n1 по n2 постов ( мой маленький каприз ). Конечно же вывод ограничен 20-ю постами, т.к. в xml страничке их именно 20.

  1. if command[0] == (PREF_C+"habr"):                     
  2.             rss = feedparser.parse('http://habrahabr.ru/rss')#забираем xml страничку и парсим
  3.             feeds = [0,20]                                                        #Это у нас для дополнительного функционала )
  4.             if len(command) == 2:
  5.                 try:
  6.                     feeds[1] = int(command[1])
  7.                 except:
  8.                     feeds[1] = 0
  9.                 if feeds[1]<1: feeds[1] = 1
  10.                 if feeds[1]>20: feeds[1] = 20
  11.             elif len(command) == 3:
  12.                 try:
  13.                     feeds[0] = int(command[1])-1
  14.                     feeds[1] = int(command[2])
  15.                 except:
  16.                     feeds[0] = 0
  17.                     feeds[1] = 19
  18.                 if feeds[0]<0: feeds[0] = 0
  19.                 if feeds[0]>19: feeds[0] = 19
  20.                 if feeds[1]<1: feeds[1] = 1
  21.                 if feeds[1]>20: feeds[1] = 20
  22.             mes = ''
  23.             for feed in range(feeds[0],feeds[1]):
  24.                 mes += rss.entries[feed].title+'\n'
  25.             self.sendMessage(user.name, mes)
* This source code was highlighted with Source Code Highlighter.


Команда !habrn n будет выводить содержимое поля description поля n

  1. elif command[0] == (PREF_C+"habrn"):
  2.             rss = feedparser.parse('http://habrahabr.ru/rss')
  3.             try:
  4.                 feedn = int(command[1])-1
  5.             except:
  6.                 feedn = 0
  7.             if feedn<0: feedn = 0
  8.             if feedn>19: feedn = 19    
  9.             self.sendMessage(user.name, rss.entries[feedn].title+'\n'+rss.entries[feedn].description)
* This source code was highlighted with Source Code Highlighter.


Тут то я хочу упомянуть про import urllib2, который я вписал в начало. Порой мне нужно узнать ip моего домашнего сервера, дабы зайти на него по ssh. Ну и конечно же не хотелось бы чтобы его адрес знал кто либо другой.

  1.         elif command[0] == (PREF_C+"ip") and user.name == '368576236':
  2.             file_s = urllib2.urlopen(urllib2.Request('http://api.wipmania.com/'))
  3.             response = file_s.read()    
  4.             self.sendMessage(user.name, response.split("<br>")[0])    
  5.             f.close()
* This source code was highlighted with Source Code Highlighter.


Если заглянуть на http://api.wipmania.com/, то можно обнаружить довольно таки простую html страничку на которой будет наш внешний ip.

Ну и заключительная часть:

  1. class BotAuth(oscar.OscarAuthenticator):
  2.     BOSClass = Bot
  3. protocol.ClientCreator(reactor, BotAuth, UIN, PASS, icq=icqMode).connectTCP(*host)
  4. reactor.run()
* This source code was highlighted with Source Code Highlighter.

Вот и все. Хочу сказать спасибо человеку, помогшему получить ip для последней команды.

Вот весь код:

  1. # -*- coding: utf-8 -*-
  2. # ICQ bot
  3.  
  4. # UIN
  5. UIN = "123456"
  6. PASS = "pass"
  7.  
  8.  
  9. # Server
  10. host = ("login.icq.com", 5238)
  11. icqMode = 1
  12.  
  13. # Status message
  14. AMSG = "I'm here +)"
  15.  
  16. from twisted.words.protocols import oscar
  17. from twisted.internet import protocol, reactor
  18. import feedparser
  19. import urllib2
  20.  
  21. # Class Bot
  22. class Bot(oscar.BOSConnection):
  23.  
  24.     capabilities = [oscar.CAP_CHAT]
  25.  
  26.     def initDone(self):
  27.         print "Connect ",UIN," to server", host[0], host[1]
  28.         
  29.         self.requestSelfInfo().addCallback(self.gotSelfInfo)
  30.         self.requestSSI().addCallback(self.gotBuddyList)
  31.  
  32.         self.setAway(AMSG)
  33.  
  34.     def gotSelfInfo(self, user):
  35.         print user.__dict__
  36.         self.name = user.name
  37.  
  38.  
  39.     def gotBuddyList(self, l):
  40.         print l
  41.         self.activateSSI()
  42.         self.setProfile("""ICQBot""")
  43.         self.setIdleTime(0)
  44.         self.clientReady()
  45.  
  46.     def gotAway(self, away, user):
  47.         if away:
  48.             print "User ", user,": ",away
  49.  
  50.     def receiveMessage(self, user, multiparts, flags):
  51.         print "\n< From: ", user.name
  52.         print "< Message: ", multiparts[0][0].decode('cp1251')
  53.         command = multiparts[0][0].lower().split(' ')
  54.                 
  55.  
  56.         PREF_C = "!"
  57.         if command[0] == (PREF_C+"habr"):
  58.             rss = feedparser.parse('http://habrahabr.ru/rss')
  59.             feeds = [0,20]
  60.             if len(command) == 2:
  61.                 try:
  62.                     feeds[1] = int(command[1])
  63.                 except:
  64.                     feeds[1] = 0
  65.  
  66.                 if feeds[1]<1: feeds[1] = 1
  67.                 if feeds[1]>20: feeds[1] = 20
  68.  
  69.             elif len(command) == 3:
  70.                 try:
  71.                     feeds[0] = int(command[1])-1
  72.                     feeds[1] = int(command[2])
  73.                 except:
  74.                     feeds[0] = 0
  75.                     feeds[1] = 19
  76.  
  77.                 if feeds[0]<0: feeds[0] = 0
  78.                 if feeds[0]>19: feeds[0] = 19
  79.                 if feeds[1]<1: feeds[1] = 1
  80.                 if feeds[1]>20: feeds[1] = 20
  81.  
  82.             mes = ''
  83.             for feed in range(feeds[0],feeds[1]):
  84.                 mes += rss.entries[feed].title+'\n'
  85.  
  86.             self.sendMessage(user.name, mes)
  87.             
  88.  
  89.         elif command[0] == (PREF_C+"habrn"):
  90.             rss = feedparser.parse('http://habrahabr.ru/rss')
  91.             try:
  92.                 feedn = int(command[1])-1
  93.             except:
  94.                 feedn = 0
  95.             if feedn<0: feedn = 0
  96.             if feedn>19: feedn = 19
  97.             
  98.             self.sendMessage(user.name, rss.entries[feedn].title+'\n'+rss.entries[feedn].description)
  99.  
  100.     
  101.         elif command[0] == (PREF_C+"ip") and user.name == '123456':
  102.             file_s = urllib2.urlopen(urllib2.Request('http://api.wipmania.com/'))
  103.             response = file_s.read()    
  104.             self.sendMessage(user.name, response.split("<br>")[0])    
  105.             f.close() 
  106.  
  107. class BotAuth(oscar.OscarAuthenticator):
  108.     BOSClass = Bot
  109.  
  110. protocol.ClientCreator(reactor, BotAuth, UIN, PASS, icq=icqMode).connectTCP(*host)
  111. reactor.run()
* This source code was highlighted with Source Code Highlighter.


Спасибо за внимание!
Метки:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 21
  • 0
    Стоит ставить пробелы после запятых в коде и занести число 20 в константу. А ещё можно использовать регулярные выражения.
    • 0
      Что если сделать бота многопоточным? Чтобы, например, в одном потоке принимал команды, в другом их обрабатывал, а в третьем возвращал результат.
      • –1
        зачем? twisted и так многопоточный
      • 0
        Смысл? Придется заморачиваться с queue, чтобы подавать задания, затем выводить ответ все равно СИНХРОННО, то есть после комманды. Если Вы хотите многопользовательский вариант, то да, лучше многопоточно сделать, но опять же получение комманды-обработка-ответ должно выполняться в одном потоке.
        С Twisted просто можно наверное нафоркать процессов и не париться
      • +2
        feedparser.parse, по всей видимости, блокирующая функция, следовательно во время ее выполнения бот не сможет обслуживать других клиентов. В данном случае нужно либо использовать какой-либо неблокирующий аналог данной функции, либо выполнять ее в другом потоке.
        • +4
          не понятно за чем это все, ради инвайта? Про твистед не рассказал, показал какой-то велосипед, когда есть готовые либы, ну и делал бы сразу на socket, интересней бы было, короче не зачет, еще одна мусорная статья ни о чем.
          • +2
            Имхо icq не очень актуален… Мне кажется проще было бы подключить жаббер и поставить rss + регулярку для обрезания)
            • +7
              Хм… Написать, что ли, и мне про своего питоновского бота для jabber… Есть желающие взглянуть на сие чудо инженерной мысли?
              • +3
                давай если код интересный
                • –2
                  давай, даже если не интересный! :) Чтобы стать хорошим программистом нужно писать и читать много кода ) А где читать код как не на хабре?))
                  • +1
                    ОК, чуток «доболею» и напишу. Собственно, код там очень маленький. Использовал библиотеку xmpppy и стандартную питоновскую smtplib для извещений на e-mail.
                    • 0
                      писал такой на первом курсе… но он куда то потерялся. Так что решил написать про что нить новонаписаное.
                      • 0
                        Jabber бот на Python (с плагинами) для администрирования компом от хабраюзера press

                        Но и на код твоего бота тоже буду рад посмотреть ;)
                      • +2
                        году в 2003-2004 я писал подобного бота. сейчас наколупал старые сырцы (почистил от лишних внешних вызовов типа походов в гугл за ответами на вопросы или рассказыванием анекдотов), если кому интересно — пользуйтесь.

                        код очень старый и страшный, ногами не бейте

                        pastebin.com/dvGDgYqs
                        • 0
                          Было бы интересно посмотреть на подобную реализацию на питоне, только работа со скайп и джаббер, как написано выше)
                          • +1
                            Jabber-бота очень легко и удобно писать на GAE, код под него будет гораздо короче.
                          • 0
                            Протокол бы без твистеда :) я gevent больше люблю
                            • 0
                              Вообще пора уже делать восстановление паролей от сайтов не на почту а в аську/жаббер :-)

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.