Add documentation to incoming IRC functions

This commit is contained in:
Dpeta 2023-02-09 23:58:03 +01:00
parent 8e2fd225a5
commit e8a283ad99
No known key found for this signature in database
GPG key ID: 51227517CEA0030C
2 changed files with 237 additions and 329 deletions

491
irc.py
View file

@ -1,12 +1,18 @@
import logging
import socket
import random
import time
import ssl
"""Provides classes and functions for communicating with an IRC server.
References for the IRC protocol:
- Original IRC protocol specification: https://www.rfc-editor.org/rfc/rfc1459
- 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 select
import socket
import ssl
import random
import datetime
import traceback
import logging
try:
from PyQt6 import QtCore, QtGui
@ -90,11 +96,16 @@ class PesterIRC(QtCore.QThread):
self.send_irc = SendIRC()
self.conn = None
self.joined = False
self.channelnames = {}
self.channel_list = []
self.channel_field = None
self.commands = {
"001": self.welcome,
"005": self.featurelist,
"221": self.umodeis,
"321": self.liststart,
"322": self.list,
"323": self.listend,
@ -216,14 +227,12 @@ class PesterIRC(QtCore.QThread):
for line in split_buffer:
line = line.decode(encoding="utf-8", errors="replace")
tags, prefix, command, args = self.parse_irc_line(line)
try:
if command:
# Only need tags with tagmsg
if command.casefold() == "tagmsg":
self.run_command(command, prefix, tags, *args)
else:
self.run_command(command, prefix, *args)
except CommandError as e:
PchumLog.warning(f"CommandError: {e}")
yield True
except socket.timeout as se:
@ -463,9 +472,7 @@ class PesterIRC(QtCore.QThread):
@QtCore.pyqtSlot(QString, bool)
def startConvo(self, handle, initiated):
self.send_irc.privmsg(
handle, "COLOR >%s" % (self.mainwindow.profile().colorcmd())
)
self.send_irc.privmsg(handle, f"COLOR >{self.mainwindow.profile().colorcmd()}")
if initiated:
self.send_irc.privmsg(handle, "PESTERCHUM:BEGIN")
@ -490,7 +497,7 @@ class PesterIRC(QtCore.QThread):
# Moods via metadata
self.send_irc.metadata("*", "set", "mood", str(me.mood.value()))
# Backwards compatibility
self.send_irc.privmsg("#pesterchum", "MOOD >%d" % (me.mood.value()))
self.send_irc.privmsg("#pesterchum", f"MOOD >{me.mood.value()}")
@QtCore.pyqtSlot()
def updateColor(self):
@ -567,6 +574,7 @@ class PesterIRC(QtCore.QThread):
self.close()
def notice(self, nick, chan, msg):
"""Standard IRC 'NOTICE' message, primarily used for automated replies from services."""
handle = nick[0 : nick.find("!")]
PchumLog.info('---> recv "NOTICE {} :{}"'.format(handle, msg))
if (
@ -579,40 +587,39 @@ class PesterIRC(QtCore.QThread):
self.noticeReceived.emit(handle, msg)
def metadata(self, target, nick, key, visibility, value):
# The format of the METADATA server notication is:
# METADATA <Target> <Key> <Visibility> <Value>
"""METADATA DRAFT metadata message from server.
The format of the METADATA server notication is:
METADATA <Target> <Key> <Visibility> <Value>
"""
if key.lower() == "mood":
try:
mood = Mood(int(value))
self.moodUpdated.emit(nick, mood)
except ValueError:
PchumLog.warning("Invalid mood value, {}, {}".format(nick, mood))
PchumLog.warning("Invalid mood value, %s, %s", nick, mood)
elif key.lower() == "color":
color = QtGui.QColor(value) # Invalid color becomes rgb 0,0,0
self.colorUpdated.emit(nick, color)
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(";")
for m in message_tags:
if m.startswith("+pesterchum"):
for tag in message_tags:
if tag.startswith("+pesterchum"):
# Pesterchum tag
try:
key, value = m.split("=")
key, value = tag.split("=")
except ValueError:
return
PchumLog.info("Pesterchum tag: {}={}".format(key, value))
PchumLog.info("Pesterchum tag: %s=%s", key, value)
# PESTERCHUM: syntax check
if (
(value == "BEGIN")
or (value == "BLOCK")
or (value == "CEASE")
or (value == "BLOCK")
or (value == "BLOCKED")
or (value == "UNBLOCK")
or (value == "IDLE")
or (value == "ME")
):
if value in ["BEGIN", "BLOCK", "CEASE", "BLOCK", "BLOCKED", "UNBLOCK", "IDLE", "ME"]:
# Process like it's a PESTERCHUM: PRIVMSG
msg = "PESTERCHUM:" + value
self.privmsg(prefix, args[0], msg)
@ -628,20 +635,20 @@ class PesterIRC(QtCore.QThread):
# 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):
# Server is ending connection.
reason = ""
for x in params:
if x:
reason += x + " "
self.stopIRC = reason.strip()
"""'ERROR' message from server, the server is terminating our connection."""
self.stopIRC = " ".join(params).strip()
self.disconnectIRC()
def privmsg(self, nick, chan, msg):
"""'PRIVMSG' message from server, the standard message."""
handle = nick[0 : nick.find("!")]
if not msg: # Length 0
return
# CTCP
# ACTION, IRC /me (The CTCP kind)
if msg[0:8] == "\x01ACTION ":
@ -726,96 +733,10 @@ class PesterIRC(QtCore.QThread):
else:
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):
"""QUIT message from server, a client has quit the server."""
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:
self.mainwindow.randhandler.setRunning(False)
server = self.mainwindow.config.server()
@ -827,11 +748,13 @@ class PesterIRC(QtCore.QThread):
self.moodUpdated.emit(handle, Mood("offline"))
def kick(self, opnick, channel, handle, reason):
"""'KICK' message from server, someone got kicked from a channel."""
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
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("!")]
PchumLog.info('---> recv "PART {}: {}"'.format(handle, channel))
self.userPresentUpdate.emit(handle, channel, "left")
@ -839,6 +762,7 @@ class PesterIRC(QtCore.QThread):
self.moodUpdated.emit(handle, Mood("offline"))
def join(self, nick, channel):
"""'JOIN' message from server, someone joined a channel."""
handle = nick[0 : nick.find("!")]
PchumLog.info('---> recv "JOIN {}: {}"'.format(handle, channel))
self.userPresentUpdate.emit(handle, channel, "join")
@ -848,15 +772,13 @@ class PesterIRC(QtCore.QThread):
self.moodUpdated.emit(handle, Mood("chummy"))
def mode(self, op, channel, mode, *handles):
"""'MODE' message from server, a user or a channel's mode changed."""
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:
handles = [""]
opnick = op[0 : op.find("!")]
PchumLog.debug("opnick=%s", opnick)
# opnick = op[0 : op.find("!")]
# Channel section
# This might be clunky with non-unrealircd IRC servers
channel_mode = ""
@ -866,7 +788,6 @@ class PesterIRC(QtCore.QThread):
modes = list(self.mainwindow.modes)
for md in unrealircd_channel_modes:
if mode.find(md) != -1: # -1 means not found
PchumLog.debug("md=" + md)
if mode[0] == "+":
modes.extend(md)
channel_mode = "+" + md
@ -879,7 +800,7 @@ class PesterIRC(QtCore.QThread):
"Can't remove channel mode that isn't set."
)
self.userPresentUpdate.emit(
"", channel, channel_mode + ":%s" % (op)
"", channel, f"{channel_mode}:{op}"
)
PchumLog.debug("pre-mode=%s", mode)
mode = mode.replace(md, "")
@ -893,28 +814,27 @@ class PesterIRC(QtCore.QThread):
if l in ["+", "-"]:
cur = l
else:
modes.append("{}{}".format(cur, l))
PchumLog.debug("handles=%s", handles)
PchumLog.debug("enumerate(modes) = " + str(list(enumerate(modes))))
modes.append(f"{cur}{l}")
for i, m in enumerate(modes):
# Server-set usermodes don't need to be passed.
if (handles == [""]) & (
("x" in m) | ("z" in m) | ("o" in m) | ("x" in m)
) != True:
if handles == [""] and not ("x" in m or "z" in m or "o" in m or "x" in m):
try:
self.userPresentUpdate.emit(handles[i], channel, m + ":%s" % (op))
self.userPresentUpdate.emit(handles[i], channel, m + f":{op}")
except IndexError as 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))
PchumLog.exception("modeSetIndexError: %s", e)
def nick(self, oldnick, newnick, hopcount=0):
PchumLog.info("{}, {}".format(oldnick, newnick))
def invite(self, sender, _you, channel):
"""'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
if oldnick == self.mainwindow.profile().handle:
# Server changed our handle, svsnick?
@ -922,14 +842,11 @@ class PesterIRC(QtCore.QThread):
# etc.
oldhandle = oldnick[0 : oldnick.find("!")]
if (oldhandle == self.mainwindow.profile().handle) or (
newnick == self.mainwindow.profile().handle
):
# print('hewwo')
if self.mainwindow.profile().handle in [newnick, oldhandle]:
self.myHandleChanged.emit(newnick)
newchum = PesterProfile(newnick, chumdb=self.mainwindow.chumdb)
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:
self.getMood(newchum)
if oldhandle == self.mainwindow.randhandler.randNick:
@ -937,134 +854,202 @@ class PesterIRC(QtCore.QThread):
elif newnick == self.mainwindow.randhandler.randNick:
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(" ")
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"):
self.channelnames = {}
if channel not in self.channelnames:
self.channelnames[channel] = []
self.channelnames[channel].extend(namelist)
# def ison(self, server, nick, nicks):
# nicklist = nicks.split(" ")
# 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):
def endofnames(self, _server, _nick, channel, _msg):
"""Numeric reply 366 RPL_ENDOFNAMES, end of NAMES list of members, usually of a channel."""
try:
namelist = self.channelnames[channel]
except KeyError:
# EON seems to return with wrong capitalization sometimes?
for cn in self.channelnames.keys():
if channel.lower() == cn.lower():
channel = cn
for channel_name in self.channelnames:
if channel.casefold() == channel_name.casefold():
channel = channel_name
namelist = self.channelnames[channel]
pl = PesterList(namelist)
del self.channelnames[channel]
self.namesReceived.emit(channel, pl)
self.namesReceived.emit(channel, PesterList(namelist))
if channel == "#pesterchum" and not self.joined:
self.joined = True
self.mainwindow.randhandler.setRunning(
self.mainwindow.randhandler.randNick in namelist
)
chums = self.mainwindow.chumList.chums
# self.isOn(*chums)
lesschums = []
for c in chums:
chandle = c.handle
if chandle in namelist:
lesschums.append(c)
for chum in chums:
if chum.handle in namelist:
lesschums.append(chum)
self.getMood(*lesschums)
def liststart(self, server, handle, *info):
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):
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):
def cannotsendtochan(self, _server, _handle, channel, msg):
"""Numeric reply 404 ERR_CANNOTSENDTOCHAN, we aren't in the channel or don't have voice."""
self.cannotSendToChan.emit(channel, msg)
def toomanypeeps(self, *stuff):
self.tooManyPeeps.emit()
def erroneusnickname(self, *args):
"""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):
# # Channel name is not valid.
# msg = ' '.join(args)
# self.forbiddenchannel.emit(channel, msg)
def nicknameinuse(self, _server, _cmd, nick, _msg):
"""Numerical reply 433 ERR_NICKNAMEINUSE, raised when changing nick to nick 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):
# Channel is forbidden.
def nickcollision(self, _server, _cmd, nick, _msg):
"""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.userPresentUpdate.emit(handle, channel, "left")
def ping(self, prefix, token):
"""Respond to server PING with PONG."""
self.send_irc.pong(token)
def inviteonlychan(self, _server, _handle, channel, _msg):
"""Numeric reply 473 ERR_INVITEONLYCHAN, can't join channel (+i)."""
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):
"""finds and runs a command"""
PchumLog.debug("processCommand {}({})".format(command, args))
"""Finds and runs a command."""
PchumLog.debug("run_command %s(%s)", command, args)
try:
f = self.commands[command]
except KeyError as e:
PchumLog.info(e)
self.__unhandled__(command, *args)
command_function = self.commands[command]
except KeyError:
PchumLog.warning("No matching function for command: %s(%s)", command, args)
return
PchumLog.debug("f %s" % f)
try:
f(*args)
except TypeError as e:
PchumLog.info(
"Failed to pass command, did the server pass an unsupported paramater? "
+ str(e)
)
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))
command_function(*args)
except TypeError:
PchumLog.exception("Failed to pass command, did the server pass an unsupported paramater?")
except Exception:
PchumLog.exception("Exception while parsing command.")
moodUpdated = QtCore.pyqtSignal("QString", Mood)
colorUpdated = QtCore.pyqtSignal("QString", QtGui.QColor)
@ -1084,13 +1069,11 @@ class PesterIRC(QtCore.QThread):
askToConnect = QtCore.pyqtSignal(Exception)
userPresentUpdate = QtCore.pyqtSignal("QString", "QString", "QString")
cannotSendToChan = QtCore.pyqtSignal("QString", "QString")
tooManyPeeps = QtCore.pyqtSignal()
quirkDisable = QtCore.pyqtSignal("QString", "QString", "QString")
signal_forbiddenchannel = QtCore.pyqtSignal("QString", "QString")
class SendIRC:
"""Provides functions for outgoing IRC commands."""
def __init__(self):
self.socket = None # INET socket connected with server.

View file

@ -4363,80 +4363,6 @@ class MainProgram(QtCore.QObject):
def trayMessageClick(self):
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):
# IRC --> Main window
return (
@ -4484,7 +4410,6 @@ class MainProgram(QtCore.QObject):
(irc.modesUpdated, widget.modesUpdated),
(irc.cannotSendToChan, widget.cannotSendToChan),
(irc.signal_forbiddenchannel, widget.forbiddenchannel),
(irc.tooManyPeeps, widget.tooManyPeeps),
(irc.quirkDisable, widget.quirkDisable),
)