Add documentation to incoming IRC functions
This commit is contained in:
parent
8e2fd225a5
commit
e8a283ad99
2 changed files with 237 additions and 329 deletions
491
irc.py
491
irc.py
|
@ -1,12 +1,18 @@
|
||||||
import logging
|
"""Provides classes and functions for communicating with an IRC server.
|
||||||
import socket
|
|
||||||
import random
|
References for the IRC protocol:
|
||||||
import time
|
- Original IRC protocol specification: https://www.rfc-editor.org/rfc/rfc1459
|
||||||
import ssl
|
- Updated IRC Client specification: https://www.rfc-editor.org/rfc/rfc2812
|
||||||
|
- Modern IRC client protocol writeup: https://modern.ircdocs.horse
|
||||||
|
- IRCv3 protocol additions: https://ircv3.net/irc/
|
||||||
|
- Draft of metadata specification: https://gist.github.com/k4bek4be/92c2937cefd49990fbebd001faf2b237
|
||||||
|
"""
|
||||||
import sys
|
import sys
|
||||||
import select
|
import socket
|
||||||
|
import ssl
|
||||||
|
import random
|
||||||
import datetime
|
import datetime
|
||||||
import traceback
|
import logging
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from PyQt6 import QtCore, QtGui
|
from PyQt6 import QtCore, QtGui
|
||||||
|
@ -90,11 +96,16 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.send_irc = SendIRC()
|
self.send_irc = SendIRC()
|
||||||
|
|
||||||
self.conn = None
|
self.conn = None
|
||||||
|
|
||||||
self.joined = False
|
self.joined = False
|
||||||
|
self.channelnames = {}
|
||||||
|
self.channel_list = []
|
||||||
|
self.channel_field = None
|
||||||
|
|
||||||
self.commands = {
|
self.commands = {
|
||||||
"001": self.welcome,
|
"001": self.welcome,
|
||||||
"005": self.featurelist,
|
"005": self.featurelist,
|
||||||
|
"221": self.umodeis,
|
||||||
"321": self.liststart,
|
"321": self.liststart,
|
||||||
"322": self.list,
|
"322": self.list,
|
||||||
"323": self.listend,
|
"323": self.listend,
|
||||||
|
@ -216,14 +227,12 @@ class PesterIRC(QtCore.QThread):
|
||||||
for line in split_buffer:
|
for line in split_buffer:
|
||||||
line = line.decode(encoding="utf-8", errors="replace")
|
line = line.decode(encoding="utf-8", errors="replace")
|
||||||
tags, prefix, command, args = self.parse_irc_line(line)
|
tags, prefix, command, args = self.parse_irc_line(line)
|
||||||
try:
|
if command:
|
||||||
# Only need tags with tagmsg
|
# Only need tags with tagmsg
|
||||||
if command.casefold() == "tagmsg":
|
if command.casefold() == "tagmsg":
|
||||||
self.run_command(command, prefix, tags, *args)
|
self.run_command(command, prefix, tags, *args)
|
||||||
else:
|
else:
|
||||||
self.run_command(command, prefix, *args)
|
self.run_command(command, prefix, *args)
|
||||||
except CommandError as e:
|
|
||||||
PchumLog.warning(f"CommandError: {e}")
|
|
||||||
|
|
||||||
yield True
|
yield True
|
||||||
except socket.timeout as se:
|
except socket.timeout as se:
|
||||||
|
@ -463,9 +472,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
|
|
||||||
@QtCore.pyqtSlot(QString, bool)
|
@QtCore.pyqtSlot(QString, bool)
|
||||||
def startConvo(self, handle, initiated):
|
def startConvo(self, handle, initiated):
|
||||||
self.send_irc.privmsg(
|
self.send_irc.privmsg(handle, f"COLOR >{self.mainwindow.profile().colorcmd()}")
|
||||||
handle, "COLOR >%s" % (self.mainwindow.profile().colorcmd())
|
|
||||||
)
|
|
||||||
if initiated:
|
if initiated:
|
||||||
self.send_irc.privmsg(handle, "PESTERCHUM:BEGIN")
|
self.send_irc.privmsg(handle, "PESTERCHUM:BEGIN")
|
||||||
|
|
||||||
|
@ -490,7 +497,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
# Moods via metadata
|
# Moods via metadata
|
||||||
self.send_irc.metadata("*", "set", "mood", str(me.mood.value()))
|
self.send_irc.metadata("*", "set", "mood", str(me.mood.value()))
|
||||||
# Backwards compatibility
|
# Backwards compatibility
|
||||||
self.send_irc.privmsg("#pesterchum", "MOOD >%d" % (me.mood.value()))
|
self.send_irc.privmsg("#pesterchum", f"MOOD >{me.mood.value()}")
|
||||||
|
|
||||||
@QtCore.pyqtSlot()
|
@QtCore.pyqtSlot()
|
||||||
def updateColor(self):
|
def updateColor(self):
|
||||||
|
@ -567,6 +574,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def notice(self, nick, chan, msg):
|
def notice(self, nick, chan, msg):
|
||||||
|
"""Standard IRC 'NOTICE' message, primarily used for automated replies from services."""
|
||||||
handle = nick[0 : nick.find("!")]
|
handle = nick[0 : nick.find("!")]
|
||||||
PchumLog.info('---> recv "NOTICE {} :{}"'.format(handle, msg))
|
PchumLog.info('---> recv "NOTICE {} :{}"'.format(handle, msg))
|
||||||
if (
|
if (
|
||||||
|
@ -579,40 +587,39 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.noticeReceived.emit(handle, msg)
|
self.noticeReceived.emit(handle, msg)
|
||||||
|
|
||||||
def metadata(self, target, nick, key, visibility, value):
|
def metadata(self, target, nick, key, visibility, value):
|
||||||
# The format of the METADATA server notication is:
|
"""METADATA DRAFT metadata message from server.
|
||||||
# METADATA <Target> <Key> <Visibility> <Value>
|
|
||||||
|
The format of the METADATA server notication is:
|
||||||
|
METADATA <Target> <Key> <Visibility> <Value>
|
||||||
|
"""
|
||||||
if key.lower() == "mood":
|
if key.lower() == "mood":
|
||||||
try:
|
try:
|
||||||
mood = Mood(int(value))
|
mood = Mood(int(value))
|
||||||
self.moodUpdated.emit(nick, mood)
|
self.moodUpdated.emit(nick, mood)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
PchumLog.warning("Invalid mood value, {}, {}".format(nick, mood))
|
PchumLog.warning("Invalid mood value, %s, %s", nick, mood)
|
||||||
elif key.lower() == "color":
|
elif key.lower() == "color":
|
||||||
color = QtGui.QColor(value) # Invalid color becomes rgb 0,0,0
|
color = QtGui.QColor(value) # Invalid color becomes rgb 0,0,0
|
||||||
self.colorUpdated.emit(nick, color)
|
self.colorUpdated.emit(nick, color)
|
||||||
|
|
||||||
def tagmsg(self, prefix, tags, *args):
|
def tagmsg(self, prefix, tags, *args):
|
||||||
PchumLog.info("TAGMSG: {} {} {}".format(prefix, tags, str(args)))
|
"""IRCv3 'TAGMSG' message/command, contains a tag without a command.
|
||||||
|
|
||||||
|
For reference see:
|
||||||
|
https://ircv3.net/specs/extensions/message-tags.html#the-tagmsg-tag-only-message
|
||||||
|
"""
|
||||||
|
PchumLog.info("TAGMSG: %s %s %s", prefix, tags, args)
|
||||||
message_tags = tags[1:].split(";")
|
message_tags = tags[1:].split(";")
|
||||||
for m in message_tags:
|
for tag in message_tags:
|
||||||
if m.startswith("+pesterchum"):
|
if tag.startswith("+pesterchum"):
|
||||||
# Pesterchum tag
|
# Pesterchum tag
|
||||||
try:
|
try:
|
||||||
key, value = m.split("=")
|
key, value = tag.split("=")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
PchumLog.info("Pesterchum tag: {}={}".format(key, value))
|
PchumLog.info("Pesterchum tag: %s=%s", key, value)
|
||||||
# PESTERCHUM: syntax check
|
# PESTERCHUM: syntax check
|
||||||
if (
|
if value in ["BEGIN", "BLOCK", "CEASE", "BLOCK", "BLOCKED", "UNBLOCK", "IDLE", "ME"]:
|
||||||
(value == "BEGIN")
|
|
||||||
or (value == "BLOCK")
|
|
||||||
or (value == "CEASE")
|
|
||||||
or (value == "BLOCK")
|
|
||||||
or (value == "BLOCKED")
|
|
||||||
or (value == "UNBLOCK")
|
|
||||||
or (value == "IDLE")
|
|
||||||
or (value == "ME")
|
|
||||||
):
|
|
||||||
# Process like it's a PESTERCHUM: PRIVMSG
|
# Process like it's a PESTERCHUM: PRIVMSG
|
||||||
msg = "PESTERCHUM:" + value
|
msg = "PESTERCHUM:" + value
|
||||||
self.privmsg(prefix, args[0], msg)
|
self.privmsg(prefix, args[0], msg)
|
||||||
|
@ -628,20 +635,20 @@ class PesterIRC(QtCore.QThread):
|
||||||
# Invalid syntax
|
# Invalid syntax
|
||||||
PchumLog.warning("TAGMSG with invalid syntax.")
|
PchumLog.warning("TAGMSG with invalid syntax.")
|
||||||
|
|
||||||
|
def ping(self, _prefix, token):
|
||||||
|
"""'PING' command from server, we respond with PONG and a matching token."""
|
||||||
|
self.send_irc.pong(token)
|
||||||
|
|
||||||
def error(self, *params):
|
def error(self, *params):
|
||||||
# Server is ending connection.
|
"""'ERROR' message from server, the server is terminating our connection."""
|
||||||
reason = ""
|
self.stopIRC = " ".join(params).strip()
|
||||||
for x in params:
|
|
||||||
if x:
|
|
||||||
reason += x + " "
|
|
||||||
self.stopIRC = reason.strip()
|
|
||||||
self.disconnectIRC()
|
self.disconnectIRC()
|
||||||
|
|
||||||
def privmsg(self, nick, chan, msg):
|
def privmsg(self, nick, chan, msg):
|
||||||
|
"""'PRIVMSG' message from server, the standard message."""
|
||||||
handle = nick[0 : nick.find("!")]
|
handle = nick[0 : nick.find("!")]
|
||||||
if not msg: # Length 0
|
if not msg: # Length 0
|
||||||
return
|
return
|
||||||
|
|
||||||
# CTCP
|
# CTCP
|
||||||
# ACTION, IRC /me (The CTCP kind)
|
# ACTION, IRC /me (The CTCP kind)
|
||||||
if msg[0:8] == "\x01ACTION ":
|
if msg[0:8] == "\x01ACTION ":
|
||||||
|
@ -726,96 +733,10 @@ class PesterIRC(QtCore.QThread):
|
||||||
else:
|
else:
|
||||||
self.messageReceived.emit(handle, msg)
|
self.messageReceived.emit(handle, msg)
|
||||||
|
|
||||||
def welcome(self, server, nick, msg):
|
|
||||||
self.setConnected()
|
|
||||||
# mychumhandle = self.mainwindow.profile().handle
|
|
||||||
mymood = self.mainwindow.profile().mood.value()
|
|
||||||
color = self.mainwindow.profile().color
|
|
||||||
if not self.mainwindow.config.lowBandwidth():
|
|
||||||
# Negotiate capabilities
|
|
||||||
self.send_irc.cap("REQ", "message-tags")
|
|
||||||
self.send_irc.cap(
|
|
||||||
self, "REQ", "draft/metadata-notify-2"
|
|
||||||
) # <--- Not required in the unreal5 module implementation
|
|
||||||
self.send_irc.cap(
|
|
||||||
self, "REQ", "pesterchum-tag"
|
|
||||||
) # <--- Currently not using this
|
|
||||||
time.sleep(0.413 + 0.097) # <--- somehow, this actually helps.
|
|
||||||
self.send_irc.join("#pesterchum")
|
|
||||||
# Moods via metadata
|
|
||||||
self.send_irc.metadata("*", "sub", "mood")
|
|
||||||
self.send_irc.metadata("*", "set", "mood", str(mymood))
|
|
||||||
# Color via metadata
|
|
||||||
self.send_irc.metadata("*", "sub", "color")
|
|
||||||
self.send_irc.metadata("*", "set", "color", str(color.name()))
|
|
||||||
# Backwards compatible moods
|
|
||||||
self.send_irc.privmsg("#pesterchum", "MOOD >%d" % (mymood))
|
|
||||||
|
|
||||||
def erroneusnickname(self, *args):
|
|
||||||
"""RFC 432"""
|
|
||||||
# Server is not allowing us to connect.
|
|
||||||
reason = "Handle is not allowed on this server.\n"
|
|
||||||
for x in args:
|
|
||||||
if x:
|
|
||||||
reason += x + " "
|
|
||||||
self.stopIRC = reason.strip()
|
|
||||||
self.disconnectIRC()
|
|
||||||
|
|
||||||
def keyvalue(self, target, handle_us, handle_owner, key, visibility, *value):
|
|
||||||
# The format of the METADATA server notication is:
|
|
||||||
# METADATA <Target> <Key> <Visibility> <Value>
|
|
||||||
if key == "mood":
|
|
||||||
mood = Mood(int(value[0]))
|
|
||||||
self.moodUpdated.emit(handle_owner, mood)
|
|
||||||
|
|
||||||
def metadatasubok(self, *params):
|
|
||||||
PchumLog.info("metadatasubok: %s", params)
|
|
||||||
|
|
||||||
def nomatchingkey(self, target, our_handle, failed_handle, key, *error):
|
|
||||||
# Try to get moods the old way if metadata fails.
|
|
||||||
PchumLog.info("nomatchingkey: " + failed_handle)
|
|
||||||
# No point in GETMOOD-ing services
|
|
||||||
if failed_handle.casefold() not in SERVICES:
|
|
||||||
self.send_irc.privmsg("#pesterchum", f"GETMOOD {failed_handle}")
|
|
||||||
|
|
||||||
def keynotset(self, target, our_handle, failed_handle, key, *error):
|
|
||||||
# Try to get moods the old way if metadata fails.
|
|
||||||
PchumLog.info("nomatchingkey: %s", failed_handle)
|
|
||||||
self.send_irc.privmsg("#pesterchum", f"GETMOOD {failed_handle}")
|
|
||||||
|
|
||||||
def keynopermission(self, target, our_handle, failed_handle, key, *error):
|
|
||||||
# Try to get moods the old way if metadata fails.
|
|
||||||
PchumLog.info("nomatchingkey: %s", failed_handle)
|
|
||||||
self.send_irc.privmsg("#pesterchum", f"GETMOOD {failed_handle}")
|
|
||||||
|
|
||||||
def featurelist(self, target, handle, *params):
|
|
||||||
# Better to do this via CAP ACK/CAP NEK
|
|
||||||
# RPL_ISUPPORT
|
|
||||||
features = params[:-1]
|
|
||||||
PchumLog.info("Server featurelist: " + str(features))
|
|
||||||
for x in features:
|
|
||||||
if x.upper().startswith("METADATA"):
|
|
||||||
PchumLog.info("Server supports metadata.")
|
|
||||||
self.metadata_supported = True
|
|
||||||
|
|
||||||
def cap(self, server, nick, subcommand, tag):
|
|
||||||
PchumLog.info("CAP {} {} {} {}".format(server, nick, subcommand, tag))
|
|
||||||
# if tag == "message-tags":
|
|
||||||
# if subcommand == "ACK":
|
|
||||||
|
|
||||||
def nicknameinuse(self, server, cmd, nick, msg):
|
|
||||||
newnick = "pesterClient%d" % (random.randint(100, 999))
|
|
||||||
self.send_irc.nick(newnick)
|
|
||||||
self.nickCollision.emit(nick, newnick)
|
|
||||||
|
|
||||||
def nickcollision(self, server, cmd, nick, msg):
|
|
||||||
newnick = "pesterClient%d" % (random.randint(100, 999))
|
|
||||||
self.send_irc.nick(newnick)
|
|
||||||
self.nickCollision.emit(nick, newnick)
|
|
||||||
|
|
||||||
def quit(self, nick, reason):
|
def quit(self, nick, reason):
|
||||||
|
"""QUIT message from server, a client has quit the server."""
|
||||||
handle = nick[0 : nick.find("!")]
|
handle = nick[0 : nick.find("!")]
|
||||||
PchumLog.info('---> recv "QUIT {}: {}"'.format(handle, reason))
|
PchumLog.info('---> recv "QUIT %s: %s"', handle, reason)
|
||||||
if handle == self.mainwindow.randhandler.randNick:
|
if handle == self.mainwindow.randhandler.randNick:
|
||||||
self.mainwindow.randhandler.setRunning(False)
|
self.mainwindow.randhandler.setRunning(False)
|
||||||
server = self.mainwindow.config.server()
|
server = self.mainwindow.config.server()
|
||||||
|
@ -827,11 +748,13 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.moodUpdated.emit(handle, Mood("offline"))
|
self.moodUpdated.emit(handle, Mood("offline"))
|
||||||
|
|
||||||
def kick(self, opnick, channel, handle, reason):
|
def kick(self, opnick, channel, handle, reason):
|
||||||
|
"""'KICK' message from server, someone got kicked from a channel."""
|
||||||
op = opnick[0 : opnick.find("!")]
|
op = opnick[0 : opnick.find("!")]
|
||||||
self.userPresentUpdate.emit(handle, channel, "kick:{}:{}".format(op, reason))
|
self.userPresentUpdate.emit(handle, channel, f"kick:{op}:{reason}")
|
||||||
# ok i shouldnt be overloading that but am lazy
|
# ok i shouldnt be overloading that but am lazy
|
||||||
|
|
||||||
def part(self, nick, channel, reason="nanchos"):
|
def part(self, nick, channel, _reason="nanchos"):
|
||||||
|
"""'PART' message from server, someone left a channel."""
|
||||||
handle = nick[0 : nick.find("!")]
|
handle = nick[0 : nick.find("!")]
|
||||||
PchumLog.info('---> recv "PART {}: {}"'.format(handle, channel))
|
PchumLog.info('---> recv "PART {}: {}"'.format(handle, channel))
|
||||||
self.userPresentUpdate.emit(handle, channel, "left")
|
self.userPresentUpdate.emit(handle, channel, "left")
|
||||||
|
@ -839,6 +762,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.moodUpdated.emit(handle, Mood("offline"))
|
self.moodUpdated.emit(handle, Mood("offline"))
|
||||||
|
|
||||||
def join(self, nick, channel):
|
def join(self, nick, channel):
|
||||||
|
"""'JOIN' message from server, someone joined a channel."""
|
||||||
handle = nick[0 : nick.find("!")]
|
handle = nick[0 : nick.find("!")]
|
||||||
PchumLog.info('---> recv "JOIN {}: {}"'.format(handle, channel))
|
PchumLog.info('---> recv "JOIN {}: {}"'.format(handle, channel))
|
||||||
self.userPresentUpdate.emit(handle, channel, "join")
|
self.userPresentUpdate.emit(handle, channel, "join")
|
||||||
|
@ -848,15 +772,13 @@ class PesterIRC(QtCore.QThread):
|
||||||
self.moodUpdated.emit(handle, Mood("chummy"))
|
self.moodUpdated.emit(handle, Mood("chummy"))
|
||||||
|
|
||||||
def mode(self, op, channel, mode, *handles):
|
def mode(self, op, channel, mode, *handles):
|
||||||
|
"""'MODE' message from server, a user or a channel's mode changed."""
|
||||||
PchumLog.debug(
|
PchumLog.debug(
|
||||||
"op=%s, channel=%s, mode=%s, handles=%s", op, channel, mode, handles
|
"mode(op=%s, channel=%s, mode=%s, handles=%s)", op, channel, mode, handles
|
||||||
)
|
)
|
||||||
|
|
||||||
if not handles:
|
if not handles:
|
||||||
handles = [""]
|
handles = [""]
|
||||||
opnick = op[0 : op.find("!")]
|
# opnick = op[0 : op.find("!")]
|
||||||
PchumLog.debug("opnick=%s", opnick)
|
|
||||||
|
|
||||||
# Channel section
|
# Channel section
|
||||||
# This might be clunky with non-unrealircd IRC servers
|
# This might be clunky with non-unrealircd IRC servers
|
||||||
channel_mode = ""
|
channel_mode = ""
|
||||||
|
@ -866,7 +788,6 @@ class PesterIRC(QtCore.QThread):
|
||||||
modes = list(self.mainwindow.modes)
|
modes = list(self.mainwindow.modes)
|
||||||
for md in unrealircd_channel_modes:
|
for md in unrealircd_channel_modes:
|
||||||
if mode.find(md) != -1: # -1 means not found
|
if mode.find(md) != -1: # -1 means not found
|
||||||
PchumLog.debug("md=" + md)
|
|
||||||
if mode[0] == "+":
|
if mode[0] == "+":
|
||||||
modes.extend(md)
|
modes.extend(md)
|
||||||
channel_mode = "+" + md
|
channel_mode = "+" + md
|
||||||
|
@ -879,7 +800,7 @@ class PesterIRC(QtCore.QThread):
|
||||||
"Can't remove channel mode that isn't set."
|
"Can't remove channel mode that isn't set."
|
||||||
)
|
)
|
||||||
self.userPresentUpdate.emit(
|
self.userPresentUpdate.emit(
|
||||||
"", channel, channel_mode + ":%s" % (op)
|
"", channel, f"{channel_mode}:{op}"
|
||||||
)
|
)
|
||||||
PchumLog.debug("pre-mode=%s", mode)
|
PchumLog.debug("pre-mode=%s", mode)
|
||||||
mode = mode.replace(md, "")
|
mode = mode.replace(md, "")
|
||||||
|
@ -893,28 +814,27 @@ class PesterIRC(QtCore.QThread):
|
||||||
if l in ["+", "-"]:
|
if l in ["+", "-"]:
|
||||||
cur = l
|
cur = l
|
||||||
else:
|
else:
|
||||||
modes.append("{}{}".format(cur, l))
|
modes.append(f"{cur}{l}")
|
||||||
PchumLog.debug("handles=%s", handles)
|
|
||||||
PchumLog.debug("enumerate(modes) = " + str(list(enumerate(modes))))
|
|
||||||
for i, m in enumerate(modes):
|
for i, m in enumerate(modes):
|
||||||
# Server-set usermodes don't need to be passed.
|
# Server-set usermodes don't need to be passed.
|
||||||
if (handles == [""]) & (
|
if handles == [""] and not ("x" in m or "z" in m or "o" in m or "x" in m):
|
||||||
("x" in m) | ("z" in m) | ("o" in m) | ("x" in m)
|
|
||||||
) != True:
|
|
||||||
try:
|
try:
|
||||||
self.userPresentUpdate.emit(handles[i], channel, m + ":%s" % (op))
|
self.userPresentUpdate.emit(handles[i], channel, m + f":{op}")
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
PchumLog.exception("modeSetIndexError: %s" % e)
|
PchumLog.exception("modeSetIndexError: %s", e)
|
||||||
# print("i = " + i)
|
|
||||||
# print("m = " + m)
|
|
||||||
# self.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op))
|
|
||||||
# self.userPresentUpdate.emit(handles[i], channel, m+":%s" % (op))
|
|
||||||
# Passing an empty handle here might cause a crash.
|
|
||||||
# except IndexError:
|
|
||||||
# self.userPresentUpdate.emit("", channel, m+":%s" % (op))
|
|
||||||
|
|
||||||
def nick(self, oldnick, newnick, hopcount=0):
|
def invite(self, sender, _you, channel):
|
||||||
PchumLog.info("{}, {}".format(oldnick, newnick))
|
"""'INVITE' message from server, someone invited us to a channel.
|
||||||
|
|
||||||
|
Pizza party everyone invited!!!"""
|
||||||
|
handle = sender.split("!")[0]
|
||||||
|
self.inviteReceived.emit(handle, channel)
|
||||||
|
|
||||||
|
def nick(self, oldnick, newnick, _hopcount=0):
|
||||||
|
"""'NICK' message from server, signifies a nick change.
|
||||||
|
|
||||||
|
Is send when our or someone else's nick got changed willingly or unwillingly."""
|
||||||
|
PchumLog.info(oldnick, newnick)
|
||||||
# svsnick
|
# svsnick
|
||||||
if oldnick == self.mainwindow.profile().handle:
|
if oldnick == self.mainwindow.profile().handle:
|
||||||
# Server changed our handle, svsnick?
|
# Server changed our handle, svsnick?
|
||||||
|
@ -922,14 +842,11 @@ class PesterIRC(QtCore.QThread):
|
||||||
|
|
||||||
# etc.
|
# etc.
|
||||||
oldhandle = oldnick[0 : oldnick.find("!")]
|
oldhandle = oldnick[0 : oldnick.find("!")]
|
||||||
if (oldhandle == self.mainwindow.profile().handle) or (
|
if self.mainwindow.profile().handle in [newnick, oldhandle]:
|
||||||
newnick == self.mainwindow.profile().handle
|
|
||||||
):
|
|
||||||
# print('hewwo')
|
|
||||||
self.myHandleChanged.emit(newnick)
|
self.myHandleChanged.emit(newnick)
|
||||||
newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
|
newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
|
||||||
self.moodUpdated.emit(oldhandle, Mood("offline"))
|
self.moodUpdated.emit(oldhandle, Mood("offline"))
|
||||||
self.userPresentUpdate.emit("{}:{}".format(oldhandle, newnick), "", "nick")
|
self.userPresentUpdate.emit(f"{oldhandle}:{newnick}", "", "nick")
|
||||||
if newnick in self.mainwindow.chumList.chums:
|
if newnick in self.mainwindow.chumList.chums:
|
||||||
self.getMood(newchum)
|
self.getMood(newchum)
|
||||||
if oldhandle == self.mainwindow.randhandler.randNick:
|
if oldhandle == self.mainwindow.randhandler.randNick:
|
||||||
|
@ -937,134 +854,202 @@ class PesterIRC(QtCore.QThread):
|
||||||
elif newnick == self.mainwindow.randhandler.randNick:
|
elif newnick == self.mainwindow.randhandler.randNick:
|
||||||
self.mainwindow.randhandler.setRunning(True)
|
self.mainwindow.randhandler.setRunning(True)
|
||||||
|
|
||||||
def namreply(self, server, nick, op, channel, names):
|
def welcome(self, server, nick, msg):
|
||||||
|
"""Numeric reply 001 RPL_WELCOME, send when we've connected to the server."""
|
||||||
|
self.setConnected()
|
||||||
|
# mychumhandle = self.mainwindow.profile().handle
|
||||||
|
mymood = self.mainwindow.profile().mood.value()
|
||||||
|
color = self.mainwindow.profile().color
|
||||||
|
if not self.mainwindow.config.lowBandwidth():
|
||||||
|
# Negotiate capabilities
|
||||||
|
self.send_irc.cap("REQ", "message-tags")
|
||||||
|
self.send_irc.cap(
|
||||||
|
self, "REQ", "draft/metadata-notify-2"
|
||||||
|
) # <--- Not required in the unreal5 module implementation
|
||||||
|
self.send_irc.cap(
|
||||||
|
self, "REQ", "pesterchum-tag"
|
||||||
|
) # <--- Currently not using this
|
||||||
|
#time.sleep(0.413 + 0.097) # <--- somehow, this actually helps.
|
||||||
|
self.send_irc.join("#pesterchum")
|
||||||
|
# Moods via metadata
|
||||||
|
self.send_irc.metadata("*", "sub", "mood")
|
||||||
|
self.send_irc.metadata("*", "set", "mood", str(mymood))
|
||||||
|
# Color via metadata
|
||||||
|
self.send_irc.metadata("*", "sub", "color")
|
||||||
|
self.send_irc.metadata("*", "set", "color", str(color.name()))
|
||||||
|
# Backwards compatible moods
|
||||||
|
self.send_irc.privmsg("#pesterchum", "MOOD >%d" % (mymood))
|
||||||
|
|
||||||
|
def featurelist(self, _target, _handle, *params):
|
||||||
|
"""Numerical reply 005 RPL_ISUPPORT to communicate supported server features.
|
||||||
|
|
||||||
|
Not in the original specification.
|
||||||
|
Metadata support could be confirmed via CAP ACK/CAP NEK.
|
||||||
|
"""
|
||||||
|
features = params[:-1]
|
||||||
|
PchumLog.info("Server featurelist: %s", features)
|
||||||
|
for x in features:
|
||||||
|
if x.upper().startswith("METADATA"):
|
||||||
|
PchumLog.info("Server supports metadata.")
|
||||||
|
self.metadata_supported = True
|
||||||
|
|
||||||
|
def cap(self, server, nick, subcommand, tag):
|
||||||
|
"""IRCv3 capabilities command from server.
|
||||||
|
|
||||||
|
See: https://ircv3.net/specs/extensions/capability-negotiation
|
||||||
|
"""
|
||||||
|
PchumLog.info("CAP %s %s %s %s", server, nick, subcommand, tag)
|
||||||
|
# if tag == "message-tags":
|
||||||
|
# if subcommand == "ACK":
|
||||||
|
|
||||||
|
def umodeis(self, _server, _handle, modes):
|
||||||
|
"""Numeric reply 221 RPL_UMODEIS, shows us our user modes."""
|
||||||
|
self.mainwindow.modes = modes
|
||||||
|
|
||||||
|
def liststart(self, _server, _handle, *info):
|
||||||
|
"""Numeric reply 321 RPL_LISTSTART, start of list of channels."""
|
||||||
|
self.channel_list = []
|
||||||
|
info = list(info)
|
||||||
|
self.channel_field = info.index("Channel") # dunno if this is protocol
|
||||||
|
PchumLog.info('---> recv "CHANNELS: %s ', self.channel_field)
|
||||||
|
|
||||||
|
def list(self, _server, _handle, *info):
|
||||||
|
"""Numeric reply 322 RPL_LIST, returns part of the list of channels."""
|
||||||
|
channel = info[self.channel_field]
|
||||||
|
usercount = info[1]
|
||||||
|
if channel not in self.channel_list and channel != "#pesterchum":
|
||||||
|
self.channel_list.append((channel, usercount))
|
||||||
|
PchumLog.info('---> recv "CHANNELS: %s ', channel)
|
||||||
|
|
||||||
|
def listend(self, _server, _handle, _msg):
|
||||||
|
"""Numeric reply 323 RPL_LISTEND, end of a series of LIST replies."""
|
||||||
|
PchumLog.info('---> recv "CHANNELS END"')
|
||||||
|
self.channelListReceived.emit(PesterList(self.channel_list))
|
||||||
|
self.channel_list = []
|
||||||
|
|
||||||
|
def channelmodeis(self, _server, _handle, channel, modes, _mode_params=""):
|
||||||
|
"""Numeric reply 324 RPL_CHANNELMODEIS, gives channel modes."""
|
||||||
|
self.modesUpdated.emit(channel, modes)
|
||||||
|
|
||||||
|
def namreply(self, _server, _nick, _op, channel, names):
|
||||||
|
"""Numeric reply 353 RPL_NAMREPLY, part of a NAMES list of members, usually of a channel."""
|
||||||
namelist = names.split(" ")
|
namelist = names.split(" ")
|
||||||
PchumLog.info('---> recv "NAMES %s: %d names"' % (channel, len(namelist)))
|
PchumLog.info('---> recv "NAMES %s: %s names"', channel, len(namelist))
|
||||||
if not hasattr(self, "channelnames"):
|
if not hasattr(self, "channelnames"):
|
||||||
self.channelnames = {}
|
self.channelnames = {}
|
||||||
if channel not in self.channelnames:
|
if channel not in self.channelnames:
|
||||||
self.channelnames[channel] = []
|
self.channelnames[channel] = []
|
||||||
self.channelnames[channel].extend(namelist)
|
self.channelnames[channel].extend(namelist)
|
||||||
|
|
||||||
# def ison(self, server, nick, nicks):
|
def endofnames(self, _server, _nick, channel, _msg):
|
||||||
# nicklist = nicks.split(" ")
|
"""Numeric reply 366 RPL_ENDOFNAMES, end of NAMES list of members, usually of a channel."""
|
||||||
# getglub = "GETMOOD "
|
|
||||||
# PchumLog.info("---> recv \"ISON :%s\"" % nicks)
|
|
||||||
# for nick_it in nicklist:
|
|
||||||
# self.moodUpdated.emit(nick_it, Mood(0))
|
|
||||||
# if nick_it in self.mainwindow.namesdb["#pesterchum"]:
|
|
||||||
# getglub += nick_it
|
|
||||||
# if getglub != "GETMOOD ":
|
|
||||||
# self.send_irc.privmsg("#pesterchum", getglub)
|
|
||||||
|
|
||||||
def endofnames(self, server, nick, channel, msg):
|
|
||||||
try:
|
try:
|
||||||
namelist = self.channelnames[channel]
|
namelist = self.channelnames[channel]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# EON seems to return with wrong capitalization sometimes?
|
# EON seems to return with wrong capitalization sometimes?
|
||||||
for cn in self.channelnames.keys():
|
for channel_name in self.channelnames:
|
||||||
if channel.lower() == cn.lower():
|
if channel.casefold() == channel_name.casefold():
|
||||||
channel = cn
|
channel = channel_name
|
||||||
namelist = self.channelnames[channel]
|
namelist = self.channelnames[channel]
|
||||||
pl = PesterList(namelist)
|
|
||||||
del self.channelnames[channel]
|
del self.channelnames[channel]
|
||||||
self.namesReceived.emit(channel, pl)
|
self.namesReceived.emit(channel, PesterList(namelist))
|
||||||
if channel == "#pesterchum" and not self.joined:
|
if channel == "#pesterchum" and not self.joined:
|
||||||
self.joined = True
|
self.joined = True
|
||||||
self.mainwindow.randhandler.setRunning(
|
self.mainwindow.randhandler.setRunning(
|
||||||
self.mainwindow.randhandler.randNick in namelist
|
self.mainwindow.randhandler.randNick in namelist
|
||||||
)
|
)
|
||||||
chums = self.mainwindow.chumList.chums
|
chums = self.mainwindow.chumList.chums
|
||||||
# self.isOn(*chums)
|
|
||||||
lesschums = []
|
lesschums = []
|
||||||
for c in chums:
|
for chum in chums:
|
||||||
chandle = c.handle
|
if chum.handle in namelist:
|
||||||
if chandle in namelist:
|
lesschums.append(chum)
|
||||||
lesschums.append(c)
|
|
||||||
self.getMood(*lesschums)
|
self.getMood(*lesschums)
|
||||||
|
|
||||||
def liststart(self, server, handle, *info):
|
def cannotsendtochan(self, _server, _handle, channel, msg):
|
||||||
self.channel_list = []
|
"""Numeric reply 404 ERR_CANNOTSENDTOCHAN, we aren't in the channel or don't have voice."""
|
||||||
info = list(info)
|
|
||||||
self.channel_field = info.index("Channel") # dunno if this is protocol
|
|
||||||
PchumLog.info('---> recv "CHANNELS: %s ' % (self.channel_field))
|
|
||||||
|
|
||||||
def list(self, server, handle, *info):
|
|
||||||
channel = info[self.channel_field]
|
|
||||||
usercount = info[1]
|
|
||||||
if channel not in self.channel_list and channel != "#pesterchum":
|
|
||||||
self.channel_list.append((channel, usercount))
|
|
||||||
PchumLog.info('---> recv "CHANNELS: %s ' % (channel))
|
|
||||||
|
|
||||||
def listend(self, server, handle, msg):
|
|
||||||
"""End of a LIST response, assume channel list is complete."""
|
|
||||||
pl = PesterList(self.channel_list)
|
|
||||||
PchumLog.info('---> recv "CHANNELS END"')
|
|
||||||
self.channelListReceived.emit(pl)
|
|
||||||
self.channel_list = []
|
|
||||||
|
|
||||||
def umodeis(self, server, handle, modes):
|
|
||||||
self.mainwindow.modes = modes
|
|
||||||
|
|
||||||
def invite(self, sender, you, channel):
|
|
||||||
handle = sender.split("!")[0]
|
|
||||||
self.inviteReceived.emit(handle, channel)
|
|
||||||
|
|
||||||
def inviteonlychan(self, server, handle, channel, msg):
|
|
||||||
self.chanInviteOnly.emit(channel)
|
|
||||||
|
|
||||||
# channelmodeis can have six arguments.
|
|
||||||
def channelmodeis(self, server, handle, channel, modes, mode_params=""):
|
|
||||||
self.modesUpdated.emit(channel, modes)
|
|
||||||
|
|
||||||
def cannotsendtochan(self, server, handle, channel, msg):
|
|
||||||
self.cannotSendToChan.emit(channel, msg)
|
self.cannotSendToChan.emit(channel, msg)
|
||||||
|
|
||||||
def toomanypeeps(self, *stuff):
|
def erroneusnickname(self, *args):
|
||||||
self.tooManyPeeps.emit()
|
"""Numeric reply 432 ERR_ERRONEUSNICKNAME, we have a forbidden or protocol-breaking nick."""
|
||||||
|
# Server is not allowing us to connect.
|
||||||
|
reason = "Handle is not allowed on this server.\n" + " ".join(args)
|
||||||
|
self.stopIRC = reason.strip()
|
||||||
|
self.disconnectIRC()
|
||||||
|
|
||||||
# def badchanmask(channel, *args):
|
def nicknameinuse(self, _server, _cmd, nick, _msg):
|
||||||
# # Channel name is not valid.
|
"""Numerical reply 433 ERR_NICKNAMEINUSE, raised when changing nick to nick in use."""
|
||||||
# msg = ' '.join(args)
|
newnick = "pesterClient%d" % (random.randint(100, 999))
|
||||||
# self.forbiddenchannel.emit(channel, msg)
|
self.send_irc.nick(newnick)
|
||||||
|
self.nickCollision.emit(nick, newnick)
|
||||||
|
|
||||||
def forbiddenchannel(self, server, handle, channel, msg):
|
def nickcollision(self, _server, _cmd, nick, _msg):
|
||||||
# Channel is forbidden.
|
"""Numerical reply 436 ERR_NICKCOLLISION, raised during connect when nick is in use."""
|
||||||
|
newnick = "pesterClient%d" % (random.randint(100, 999))
|
||||||
|
self.send_irc.nick(newnick)
|
||||||
|
self.nickCollision.emit(nick, newnick)
|
||||||
|
|
||||||
|
def forbiddenchannel(self, _server, handle, channel, msg):
|
||||||
|
"""Numeric reply 448 'forbiddenchannel' reply, channel is forbidden.
|
||||||
|
|
||||||
|
Not in the specification but used by UnrealIRCd."""
|
||||||
self.signal_forbiddenchannel.emit(channel, msg)
|
self.signal_forbiddenchannel.emit(channel, msg)
|
||||||
self.userPresentUpdate.emit(handle, channel, "left")
|
self.userPresentUpdate.emit(handle, channel, "left")
|
||||||
|
|
||||||
def ping(self, prefix, token):
|
def inviteonlychan(self, _server, _handle, channel, _msg):
|
||||||
"""Respond to server PING with PONG."""
|
"""Numeric reply 473 ERR_INVITEONLYCHAN, can't join channel (+i)."""
|
||||||
self.send_irc.pong(token)
|
self.chanInviteOnly.emit(channel)
|
||||||
|
|
||||||
|
def keyvalue(self, target, handle_us, handle_owner, key, visibility, *value):
|
||||||
|
"""METADATA DRAFT numeric reply 761 RPL_KEYVALUE, we received the value of a key.
|
||||||
|
|
||||||
|
The format of the METADATA server notication is:
|
||||||
|
METADATA <Target> <Key> <Visibility> <Value>
|
||||||
|
"""
|
||||||
|
if key == "mood":
|
||||||
|
mood = Mood(int(value[0]))
|
||||||
|
self.moodUpdated.emit(handle_owner, mood)
|
||||||
|
|
||||||
|
def nomatchingkey(self, _target, _our_handle, failed_handle, _key, *_error):
|
||||||
|
"""METADATA DRAFT numeric reply 766 ERR_NOMATCHINGKEY, no matching key."""
|
||||||
|
PchumLog.info("nomatchingkey: %s", failed_handle)
|
||||||
|
# No point in GETMOOD-ing services
|
||||||
|
# Fallback to the normal GETMOOD method if getting mood via metadata fails.
|
||||||
|
if failed_handle.casefold() not in SERVICES:
|
||||||
|
self.send_irc.privmsg("#pesterchum", f"GETMOOD {failed_handle}")
|
||||||
|
|
||||||
|
def keynotset(self, _target, _our_handle, failed_handle, _key, *_error):
|
||||||
|
"""METADATA DRAFT numeric reply 768 ERR_KEYNOTSET, key isn't set."""
|
||||||
|
PchumLog.info("nomatchingkey: %s", failed_handle)
|
||||||
|
# Fallback to the normal GETMOOD method if getting mood via metadata fails.
|
||||||
|
if failed_handle.casefold() not in SERVICES:
|
||||||
|
self.send_irc.privmsg("#pesterchum", f"GETMOOD {failed_handle}")
|
||||||
|
|
||||||
|
def keynopermission(self, _target, _our_handle, failed_handle, _key, *_error):
|
||||||
|
"""METADATA DRAFT numeric reply 769 ERR_KEYNOPERMISSION, no permission for key."""
|
||||||
|
PchumLog.info("nomatchingkey: %s", failed_handle)
|
||||||
|
# Fallback to the normal GETMOOD method if getting mood via metadata fails.
|
||||||
|
if failed_handle.casefold() not in SERVICES:
|
||||||
|
self.send_irc.privmsg("#pesterchum", f"GETMOOD {failed_handle}")
|
||||||
|
|
||||||
|
def metadatasubok(self, *params):
|
||||||
|
""""METADATA DRAFT numeric reply 770 RPL_METADATASUBOK, we subbed to a key."""
|
||||||
|
PchumLog.info("metadatasubok: %s", params)
|
||||||
|
|
||||||
def run_command(self, command, *args):
|
def run_command(self, command, *args):
|
||||||
"""finds and runs a command"""
|
"""Finds and runs a command."""
|
||||||
PchumLog.debug("processCommand {}({})".format(command, args))
|
PchumLog.debug("run_command %s(%s)", command, args)
|
||||||
try:
|
try:
|
||||||
f = self.commands[command]
|
command_function = self.commands[command]
|
||||||
except KeyError as e:
|
except KeyError:
|
||||||
PchumLog.info(e)
|
PchumLog.warning("No matching function for command: %s(%s)", command, args)
|
||||||
self.__unhandled__(command, *args)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
PchumLog.debug("f %s" % f)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
f(*args)
|
command_function(*args)
|
||||||
except TypeError as e:
|
except TypeError:
|
||||||
PchumLog.info(
|
PchumLog.exception("Failed to pass command, did the server pass an unsupported paramater?")
|
||||||
"Failed to pass command, did the server pass an unsupported paramater? "
|
except Exception:
|
||||||
+ str(e)
|
PchumLog.exception("Exception while parsing command.")
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
# logging.info("Failed to pass command, %s" % str(e))
|
|
||||||
PchumLog.exception("Failed to pass command")
|
|
||||||
|
|
||||||
def __unhandled__(self, cmd, *args):
|
|
||||||
"""The default handler for commands. Override this method to
|
|
||||||
apply custom behavior (example, printing) unhandled commands.
|
|
||||||
"""
|
|
||||||
PchumLog.debug("unhandled command {}({})".format(cmd, args))
|
|
||||||
|
|
||||||
moodUpdated = QtCore.pyqtSignal("QString", Mood)
|
moodUpdated = QtCore.pyqtSignal("QString", Mood)
|
||||||
colorUpdated = QtCore.pyqtSignal("QString", QtGui.QColor)
|
colorUpdated = QtCore.pyqtSignal("QString", QtGui.QColor)
|
||||||
|
@ -1084,13 +1069,11 @@ class PesterIRC(QtCore.QThread):
|
||||||
askToConnect = QtCore.pyqtSignal(Exception)
|
askToConnect = QtCore.pyqtSignal(Exception)
|
||||||
userPresentUpdate = QtCore.pyqtSignal("QString", "QString", "QString")
|
userPresentUpdate = QtCore.pyqtSignal("QString", "QString", "QString")
|
||||||
cannotSendToChan = QtCore.pyqtSignal("QString", "QString")
|
cannotSendToChan = QtCore.pyqtSignal("QString", "QString")
|
||||||
tooManyPeeps = QtCore.pyqtSignal()
|
|
||||||
quirkDisable = QtCore.pyqtSignal("QString", "QString", "QString")
|
quirkDisable = QtCore.pyqtSignal("QString", "QString", "QString")
|
||||||
signal_forbiddenchannel = QtCore.pyqtSignal("QString", "QString")
|
signal_forbiddenchannel = QtCore.pyqtSignal("QString", "QString")
|
||||||
|
|
||||||
class SendIRC:
|
class SendIRC:
|
||||||
"""Provides functions for outgoing IRC commands."""
|
"""Provides functions for outgoing IRC commands."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.socket = None # INET socket connected with server.
|
self.socket = None # INET socket connected with server.
|
||||||
|
|
||||||
|
|
|
@ -4363,80 +4363,6 @@ class MainProgram(QtCore.QObject):
|
||||||
def trayMessageClick(self):
|
def trayMessageClick(self):
|
||||||
self.widget.config.set("traymsg", False)
|
self.widget.config.set("traymsg", False)
|
||||||
|
|
||||||
widget2irc = [
|
|
||||||
("sendMessage(QString, QString)", "sendMessage(QString, QString)"),
|
|
||||||
("sendNotice(QString, QString)", "sendNotice(QString, QString)"),
|
|
||||||
("sendCTCP(QString, QString)", "sendCTCP(QString, QString)"),
|
|
||||||
("newConvoStarted(QString, bool)", "startConvo(QString, bool)"),
|
|
||||||
("convoClosed(QString)", "endConvo(QString)"),
|
|
||||||
("profileChanged()", "updateProfile()"),
|
|
||||||
("moodRequest(PyQt_PyObject)", "getMood(PyQt_PyObject)"),
|
|
||||||
("moodsRequest(PyQt_PyObject)", "getMoods(PyQt_PyObject)"),
|
|
||||||
("moodUpdated()", "updateMood()"),
|
|
||||||
("mycolorUpdated()", "updateColor()"),
|
|
||||||
("blockedChum(QString)", "blockedChum(QString)"),
|
|
||||||
("unblockedChum(QString)", "unblockedChum(QString)"),
|
|
||||||
("requestNames(QString)", "requestNames(QString)"),
|
|
||||||
("requestChannelList()", "requestChannelList()"),
|
|
||||||
("joinChannel(QString)", "joinChannel(QString)"),
|
|
||||||
("leftChannel(QString)", "leftChannel(QString)"),
|
|
||||||
("kickUser(QString, QString, QString)", "kickUser(QString, QString, QString)"),
|
|
||||||
(
|
|
||||||
"setChannelMode(QString, QString, QString)",
|
|
||||||
"setChannelMode(QString, QString, QString)",
|
|
||||||
),
|
|
||||||
("channelNames(QString)", "channelNames(QString)"),
|
|
||||||
("inviteChum(QString, QString)", "inviteChum(QString, QString)"),
|
|
||||||
("pingServer()", "pingServer()"),
|
|
||||||
("setAway(bool)", "setAway(bool)"),
|
|
||||||
("killSomeQuirks(QString, QString)", "killSomeQuirks(QString, QString)"),
|
|
||||||
("disconnectIRC()", "disconnectIRC()"),
|
|
||||||
]
|
|
||||||
# IRC --> Main window
|
|
||||||
irc2widget = [
|
|
||||||
("connected()", "connected()"),
|
|
||||||
(
|
|
||||||
"moodUpdated(QString, PyQt_PyObject)",
|
|
||||||
"updateMoodSlot(QString, PyQt_PyObject)",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"colorUpdated(QString, QtGui.QColor)",
|
|
||||||
"updateColorSlot(QString, QtGui.QColor)",
|
|
||||||
),
|
|
||||||
("messageReceived(QString, QString)", "deliverMessage(QString, QString)"),
|
|
||||||
(
|
|
||||||
"memoReceived(QString, QString, QString)",
|
|
||||||
"deliverMemo(QString, QString, QString)",
|
|
||||||
),
|
|
||||||
("noticeReceived(QString, QString)", "deliverNotice(QString, QString)"),
|
|
||||||
("inviteReceived(QString, QString)", "deliverInvite(QString, QString)"),
|
|
||||||
("nickCollision(QString, QString)", "nickCollision(QString, QString)"),
|
|
||||||
("getSvsnickedOn(QString, QString)", "getSvsnickedOn(QString, QString)"),
|
|
||||||
("myHandleChanged(QString)", "myHandleChanged(QString)"),
|
|
||||||
(
|
|
||||||
"namesReceived(QString, PyQt_PyObject)",
|
|
||||||
"updateNames(QString, PyQt_PyObject)",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"userPresentUpdate(QString, QString, QString)",
|
|
||||||
"userPresentUpdate(QString, QString, QString)",
|
|
||||||
),
|
|
||||||
("channelListReceived(PyQt_PyObject)", "updateChannelList(PyQt_PyObject)"),
|
|
||||||
(
|
|
||||||
"timeCommand(QString, QString, QString)",
|
|
||||||
"timeCommand(QString, QString, QString)",
|
|
||||||
),
|
|
||||||
("chanInviteOnly(QString)", "chanInviteOnly(QString)"),
|
|
||||||
("modesUpdated(QString, QString)", "modesUpdated(QString, QString)"),
|
|
||||||
("cannotSendToChan(QString, QString)", "cannotSendToChan(QString, QString)"),
|
|
||||||
("tooManyPeeps()", "tooManyPeeps()"),
|
|
||||||
(
|
|
||||||
"quirkDisable(QString, QString, QString)",
|
|
||||||
"quirkDisable(QString, QString, QString)",
|
|
||||||
),
|
|
||||||
("signal_forbiddenchannel(QString)", "forbiddenchannel(QString)"),
|
|
||||||
]
|
|
||||||
|
|
||||||
def ircQtConnections(self, irc, widget):
|
def ircQtConnections(self, irc, widget):
|
||||||
# IRC --> Main window
|
# IRC --> Main window
|
||||||
return (
|
return (
|
||||||
|
@ -4484,7 +4410,6 @@ class MainProgram(QtCore.QObject):
|
||||||
(irc.modesUpdated, widget.modesUpdated),
|
(irc.modesUpdated, widget.modesUpdated),
|
||||||
(irc.cannotSendToChan, widget.cannotSendToChan),
|
(irc.cannotSendToChan, widget.cannotSendToChan),
|
||||||
(irc.signal_forbiddenchannel, widget.forbiddenchannel),
|
(irc.signal_forbiddenchannel, widget.forbiddenchannel),
|
||||||
(irc.tooManyPeeps, widget.tooManyPeeps),
|
|
||||||
(irc.quirkDisable, widget.quirkDisable),
|
(irc.quirkDisable, widget.quirkDisable),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue