Я создаю IRC-бота, ориентированного на Twitch, на Python, однако он медленно получает ответы. Что я делаю не так?

Как следует из названия, хотя это также первый раз, когда я использовал Python, чтобы действительно сделать что-то большое. Я еще не совсем привык к языку, так что, вероятно, я что-то упускаю. Код довольно короткий и выглядит следующим образом, с удаленным именем пользователя и приватным доступом:

import re
import socket
import sys
import time
import string

HOST = "irc.twitch.tv"
PORT = 6667                        
NICK = ""
PASS = ""
CHAN = ""                   

RATE = (20/30) # messages per second
CHAT_MSG=re.compile(r"^:\w+!\w+@\w+\.tmi\.twitch\.tv PRIVMSG #\w+ :")

def chat(sock, msg):
    sock.send("PRIVMSG #{} :{}".format(cfg.CHAN, msg))

public = socket.socket()
public.connect((HOST, PORT))
public.send("PASS {}\r\n".format(PASS).encode("utf-8"))
public.send("NICK {}\r\n".format(NICK).encode("utf-8"))
public.send("JOIN {}\r\n".format(CHAN).encode("utf-8"))

private = socket.socket()
private.connect((HOST, PORT))
private.send("PASS {}\r\n".format(PASS).encode("utf-8"))
private.send("NICK {}\r\n".format(NICK).encode("utf-8"))
private.send("CAP REQ :twitch.tv/tags twitch.tv/commands  {}\r\n".format(CHAN).encode("utf-8"))

while True:
    channelResponse = public.recv(1024).decode("utf-8")
    privateResponse = private.recv(1024).decode("utf-8")

    if privateResponse == "PING :tmi.twitch.tv\r\n":
        private.send("PONG :tmi.twitch.tv\r\n".encode("utf-8"))
    else:
        privateResponseUsername = re.search(r"\w+", privateResponse).group(0) # return the entire match
        privateResponseMessage = CHAT_MSG.sub("", privateResponse)
        print(privateResponseUsername + ": " + privateResponseMessage)

    if channelResponse == "PING :tmi.twitch.tv\r\n":
        public.send("PONG :tmi.twitch.tv\r\n".encode("utf-8"))
    else:
        username = re.search(r"\w+", channelResponse).group(0) # return the    entire match
        message = CHAT_MSG.sub("", channelResponse)
        print(username + ": " + message)
        time.sleep(1 / RATE)

Следует упомянуть, что я следовал базовому стилю шаблона, однако он не охватывал внедрение шепота в бота, поэтому мне приходится догадываться, проводя исследование о том, как это сделать, и кажется, что наиболее рекомендуемый способ - это два подключения, одно для общего доступа, одно для частного.


person Merlin    schedule 08.05.2016    source источник


Ответы (1)


Поскольку вы структурировали свой код, вы не можете ничего получить из сокета private, пока не получите что-то из сокета public. Если бы IRC время от времени не отправлял сообщения PING, это работало бы еще хуже.

Чтобы справиться с этим, используйте select и укажите два сокета. . Как только у вас появится материал, который можно прочитать, select вернется и укажет, в каком сокете есть байты, доступные для чтения.

Этот ответ имеет общий код. Возможно, вы захотите изменить его, чтобы он выглядел примерно так:

while True:
    # this will block until at least one socket is ready
    ready_socks,_,_ = select.select([private, public], [], [])
    if private in ready_socks:
        privateResponse += private.recv()
    if public in ready_socks:
        channelResponse += public.recv()

    # check privateResponse buffer, do stuff
    # check channelResponse buffer, do stuff

Есть еще несколько вещей, которые вы должны иметь в виду:

  1. Сеть не обязана доставлять все сообщения IRC одновременно или по одному. Вы можете получать "PI", "NG :t", "mi.twitch.tv", "\r\n" отдельными сообщениями. Таким образом, вы должны накапливать байты в буфере, а затем, когда у вас есть хотя бы одно целое сообщение, обрабатывать его и удалять из буфера.
  2. Символы UTF-8 могут занимать несколько байтов и могут быть разделены сетью. Не декодируйте UTF-8, пока не будете уверены, что у вас есть целое сообщение для работы.
person Jay Kominek    schedule 08.05.2016
comment
Хорошо, я настроил эту часть, но я не уверен, как проверить массивы на наличие ответов общедоступного/частного канала в Python - в цикле while. Как лучше всего это сделать? - person Merlin; 08.05.2016
comment
массивы? вы имеете в виду буферы privateResponse и channelResponse, которые являются строками? у вас есть много вариантов. в основном они вращаются вокруг того факта, что сообщения в IRC удобно заканчиваются переводом строки. если в буфере нет новой строки, нет полных сообщений. самое простое, наверное, посмотреть, есть ли новая строка, и если есть, обработать все до нее как сообщение, а затем заменить буфер всем после новой строки. - person Jay Kominek; 08.05.2016
comment
Это сделало это. Сейчас есть первоначальная пауза, но затем оба соединения ведут себя правильно и сразу же получают ответы. - person Merlin; 09.05.2016