Blog/content/posts/2022/python/irc-bot.md

235 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "🗣️ IRC бот на Python 3"
date: 2022-09-23T22:54:34+03:00
draft: false
tags: [python, irc, tutorial]
---
## Введение
Это руководство своего рода обобщение уже размешённой в сети информации,
по написанию IRC бота.
## Протокол IRC
Протокол IRC предназначен для обмена сообщениями в режиме реального времени.
IRC использует протокол **TCP** и опционально криптографический **TLS**.
В данном руководстве для общения я буду использовать библиотеку `socket`
из страндартной библиотеки Python.
Общение с сервером осуществаляется посредством отправки команд, вот основные из них:
### `USER`
Команда, для приветствия с сервером.
```text
USER <username> <hostname> <servername> :<realname>
```
Например:
```text
USER iiiypuk localhost localhost :Alexander
```
### PASS
Пароль, если необходимо для захода на сервер.
### NICK
Команда для смены ника.
### PONG
Ответ на команду сервера `PING`.
### JOIN
Команда для входа в канал.
```text
JOIN #<chanel_name>
```
### PART
Команда, чтобы выйти из канала.
Можно указать причину ухода из канала, добисав второй необязательный аргумет.
```text
PART #support_chanel
```
### PRIVMSG
Команда, чтобы послать сообщение пользователю или на канал.
```text
PRIVMSG <user_name> :<message>
```
### QUIT
Команда, чтобы отсоединиться от сервера.
## Команды сервера
Помимо команд, которые можно отправить на сервер, клиент также получает команды от сервера.
### PING
Сервер время от времени посылает команду PING, на случай, если клиент повиснет.
Если быстро не ответить на команду пинг командой PONG, сервер разорвёт соединение.
### :
```text
:<nickname>!<username>@<hostname> <event>
```
`<event>` событие, созданное пользователем `<nickname>`.
Например `<nickname>` отправил тебе сообщение или выкинул тебя из канала.
### PRIVMSG
```text
PRIVMSG <receiver> :<message>
```
Новое сообщение, личное или в канал.
### KICK
```text
KICK <chanel> <nickname>
```
Пользователя `<nickname>` кикнули с канала `<chanel>`.
## Подключение к серверу
Определим некоторые переменные
```python
server = 'iiiypuk.me' # адрес сервера
channel = '#admin' # имя канала
botnick = 'porteus' # ник бота
```
Инициализируем socket и подключимся к серверу.
```python
import socket
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
irc.connect((server, 6667))
```
Для работы с сервером этого не достаточно, но необходимо.
Следующий шаг, это "регистрация" бота на сервере.
```python
irc.send(str.encode('USER ' + botnick + ' localhost localhost :This is a bot!\n'))
irc.send(str.encode('NICK ' + botnick + '\n'))
irc.send(str.encode('JOIN '+ channel +'\n'))
```
Обратил внимание на функцию `str.encode`, которая преобразует
**UTF-8** строку в набор байтов.
Если попытаться передать обычною строку, получим ошибку `TypeError`:
```text
Traceback (most recent call last):
File "/home/user/Temp/IRCBOT/./bot.py", line 14, in <module>
irc.send('USER ' + botnick + ' localhost localhost :This is a bot!\n')
TypeError: a bytes-like object is required, not 'str'
```
Можно добавить `b` перед строкой и использовать функцию `bytes()`
для преобразования строк в набор байтов, но я лучше воспринимаею код,
когда используется метод `encode`.
```python
irc.send(b'USER ' + bytes(botnick, encoding = 'utf-8') + b' localhost localhost :This is a bot!\n')
```
И в конечном итоге получаем сообщения от сервера.
```python
while True:
text = irc.recv(2040)
print(text)
```
Ниже представлю полный листинг программы.
```python
#!/usr/bin/env python3
import socket
server = 'iiiypuk.me'
channel = '#admin'
botnick = 'porteus'
irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Connecting to: ' + server)
irc.connect((server, 6667))
irc.send(str.encode('USER ' + botnick + ' localhost localhost :This is a bot!\n'))
irc.send(str.encode('NICK ' + botnick + '\n'))
irc.send(str.encode('JOIN '+ channel +'\n'))
while True:
text = irc.recv(2040)
print(text)
if text.find(str.encode('PING')) != -1:
irc.send(str.encode('PONG ' + str(text.split()[1]) + '\r\n'))
```
Я в главный цикл программы добавил ответ за команду сервера **PING**.
## Полезные действия
Через инстукцию `if` добавим боту полезную функцию.
Бот будет возвращать текущее время в формате UNIX.
```python
# !time - return current timestamp
if text.find(str.encode('!time')) != -1:
ts = time.time()
irc.send(str.encode('PRIVMSG #admin :{}\r\n'.format(int(ts))))
```
Код бота можно найти по этой ссылке.
[iiiypuk/snipplets.dev:Python/irc-bot.py](https://git.a2s.su/iiiypuk/snipplets.dev/src/branch/master/~/Python/irc-bot.py)
## Потребление памяти
На моей конфигурации скрипт потребляет примерно 6,8-7.0Mb.
```text
Linux porteus.example.net 5.18.8-porteus #1 SMP PREEMPT_DYNAMIC Sat Jul 2 10:05:31 MSK 2022 x86_64
Intel(R) Core(TM)2 Duo CPU T6570 @ 2.10GHz GenuineIntel GNU/Linux
```
Тот же самый код, но на [Crystal](https://crystal-lang.org/)
потребляет 1.7Mb памяти.
## Используемые материалы
* Internet Relay Chat Protocol RFC [[Ссылка]](http://web.archive.org/web/20170929015000/http://www2.irchelp.org:80/irchelp/rfc/rfc.html)
* How do i program a simple IRC bot in python? [[Ссылка]](https://stackoverflow.com/questions/2968408/how-do-i-program-a-simple-irc-bot-in-python)
* Краткое описание протокола IRC и пример бота [[Ссылка]](https://eax.me/irc-descr/)
* Простейший IRC-бот на Python, а также при чем тут Slack, Gitter и прочие веб-чаты [[Ссылка]](https://eax.me/python-irc-bot/)