IrcClient.py: split from IrcMiRCARTBot.py.

IrcMiRCARTBot.py: renamed from pngbot.py, importing IrcClient.IrcClient.
MiRCART.py: renamed from mirc2png.py.
README.md: updated.
This commit is contained in:
Lucio Andrés Illanes Albornoz 2018-01-03 02:33:12 +01:00
parent 98eae4257c
commit c32d749675
4 changed files with 122 additions and 92 deletions

110
IrcClient.py Normal file
View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
#
# mirc2png -- convert ASCII w/ mIRC control codes to monospaced PNG (for EFnet #MiRCART)
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
from itertools import chain
import select, socket, time
class IrcClient:
"""Non-blocking abstraction over the IRC protocol"""
serverHname = serverPort = None;
clientNick = clientIdent = clientGecos = None;
clientSocket = clientSocketFile = None;
clientNextTimeout = None
# {{{ connect(): Connect to server and register w/ optional timeout
def connect(self, timeout=None):
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.setblocking(0)
try:
self.clientSocket.connect((self.serverHname, int(self.serverPort)))
except BlockingIOError:
pass
if timeout:
readySet = select.select([], [self.clientSocket.fileno()], [], timeout)
if len(readySet[1]) == 0:
self.close(); return False;
else:
select.select([], [self.clientSocket.fileno()], [])
self.clientSocketFile = self.clientSocket.makefile()
self.sendline("NICK", self.clientNick)
self.sendline("USER", self.clientIdent, "0", "0", self.clientGecos)
return True
# }}}
# {{{ close(): Close connection to server
def close(self):
if self.clientSocket != None:
self.clientSocket.close()
self.clientSocket = self.clientSocketFile = None;
# }}}
# {{{ readline(): Read and parse single line from server into canonicalised list, honouring timers
def readline(self):
if self.clientNextTimeout:
timeNow = time.time()
if self.clientNextTimeout <= timeNow:
return ""
else:
readySet = select.select([self.clientSocket.fileno()], [], [], self.clientNextTimeout - timeNow)
else:
readySet = select.select([self.clientSocket.fileno()], [], [])
msg = self.clientSocketFile.readline()
if len(msg):
msg = msg.rstrip("\r\n")
else:
if len(readySet[0]) == 0:
return ""
else:
return None
msg = msg.split(" :", 1)
if len(msg) == 1:
msg = list(chain.from_iterable(m.split(" ") for m in msg))
elif len(msg) == 2:
msg = msg[0].split(" ") + [msg[1]]
if msg[0][0] == ':':
msg = [msg[0][1:]] + msg[1:]
else:
msg = [""] + msg[0:]
return msg
# }}}
# {{{ sendline(): Parse and send single line to server from list, ignoring timers
def sendline(self, *args):
msg = ""; argNumMax = len(args);
for argNum in range(0, argNumMax):
if argNum == (argNumMax - 1):
msg += ":" + args[argNum]
else:
msg += args[argNum] + " "
msg = (msg + "\r\n").encode(); msgLen = len(msg); msgBytesSent = 0;
while msgBytesSent < msgLen:
readySet = select.select([], [self.clientSocket.fileno()], [])
msgBytesSent = self.clientSocket.send(msg)
msg = msg[msgBytesSent:]; msgLen -= msgBytesSent;
# }}}
#
# Initialisation method
def __init__(self, serverHname, serverPort, clientNick, clientIdent, clientGecos):
self.serverHname = serverHname; self.serverPort = serverPort;
self.clientNick = clientNick; self.clientIdent = clientIdent; self.clientGecos = clientGecos;
# vim:expandtab foldmethod=marker sw=8 ts=8 tw=120

View File

@ -22,95 +22,13 @@
# SOFTWARE. # SOFTWARE.
# #
from itertools import chain
import base64 import base64
import errno, os, select, socket, sys, time import os, sys, time
import json import json
import mirc2png import IrcClient, MiRCART
import requests, urllib.request import requests, urllib.request
class IrcBot: class IrcMiRCARTBot(IrcClient.IrcClient):
"""Non-blocking abstraction over the IRC protocol"""
serverHname = serverPort = None;
clientNick = clientIdent = clientGecos = None;
clientSocket = clientSocketFile = None;
clientNextTimeout = None
# {{{ connect(): Connect to server and register w/ optional timeout
def connect(self, timeout=None):
self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.clientSocket.setblocking(0)
try:
self.clientSocket.connect((self.serverHname, int(self.serverPort)))
except BlockingIOError:
pass
if timeout:
readySet = select.select([], [self.clientSocket.fileno()], [], timeout)
if len(readySet[1]) == 0:
self.close(); return False;
else:
select.select([], [self.clientSocket.fileno()], [])
self.clientSocketFile = self.clientSocket.makefile()
self.sendline("NICK", self.clientNick)
self.sendline("USER", self.clientIdent, "0", "0", self.clientGecos)
return True
# }}}
# {{{ close(): Close connection to server
def close(self):
if self.clientSocket != None:
self.clientSocket.close()
self.clientSocket = self.clientSocketFile = None;
# }}}
# {{{ readline(): Read and parse single line from server into canonicalised list, honouring timers
def readline(self):
if self.clientNextTimeout:
timeNow = time.time()
if self.clientNextTimeout <= timeNow:
return ""
else:
readySet = select.select([self.clientSocket.fileno()], [], [], self.clientNextTimeout - timeNow)
else:
readySet = select.select([self.clientSocket.fileno()], [], [])
msg = self.clientSocketFile.readline()
if len(msg):
msg = msg.rstrip("\r\n")
else:
if len(readySet[0]) == 0:
return ""
else:
return None
msg = msg.split(" :", 1)
if len(msg) == 1:
msg = list(chain.from_iterable(m.split(" ") for m in msg))
elif len(msg) == 2:
msg = msg[0].split(" ") + [msg[1]]
if msg[0][0] == ':':
msg = [msg[0][1:]] + msg[1:]
else:
msg = [""] + msg[0:]
return msg
# }}}
# {{{ sendline(): Parse and send single line to server from list, ignoring timers
def sendline(self, *args):
msg = ""; argNumMax = len(args);
for argNum in range(0, argNumMax):
if argNum == (argNumMax - 1):
msg += ":" + args[argNum]
else:
msg += args[argNum] + " "
msg = (msg + "\r\n").encode(); msgLen = len(msg); msgBytesSent = 0;
while msgBytesSent < msgLen:
readySet = select.select([], [self.clientSocket.fileno()], [])
msgBytesSent = self.clientSocket.send(msg)
msg = msg[msgBytesSent:]; msgLen -= msgBytesSent;
# }}}
# {{{ Initialisation method
def __init__(self, serverHname, serverPort, clientNick, clientIdent, clientGecos):
self.serverHname = serverHname; self.serverPort = serverPort;
self.clientNick = clientNick; self.clientIdent = clientIdent; self.clientGecos = clientGecos;
# }}}
class IrcMiRCARTBot(IrcBot):
"""IRC<->MiRCART bot""" """IRC<->MiRCART bot"""
clientChannelLastMessage = clientChannelOps = clientChannel = None clientChannelLastMessage = clientChannelOps = clientChannel = None
clientChannelRejoin = None clientChannelRejoin = None
@ -277,11 +195,12 @@ class IrcMiRCARTBot(IrcBot):
else: else:
return [responseHttp.status_code] return [responseHttp.status_code]
# }}} # }}}
# {{{ Initialisation method
#
# Initialisation method
def __init__(self, serverHname, serverPort="6667", clientNick="pngbot", clientIdent="pngbot", clientGecos="pngbot", clientChannel="#MiRCART"): def __init__(self, serverHname, serverPort="6667", clientNick="pngbot", clientIdent="pngbot", clientGecos="pngbot", clientChannel="#MiRCART"):
super().__init__(serverHname, serverPort, clientNick, clientIdent, clientGecos) super().__init__(serverHname, serverPort, clientNick, clientIdent, clientGecos)
self.clientChannel = clientChannel self.clientChannel = clientChannel
# }}}
# #
# Entry point # Entry point

View File

@ -165,7 +165,9 @@ class MiRCART:
self.outCurColourBg = 1; self.outCurColourFg = 15; self.outCurColourBg = 1; self.outCurColourFg = 15;
self.inCurColourSpec = ""; self.state = self.State.STATE_CHAR; self.inCurColourSpec = ""; self.state = self.State.STATE_CHAR;
# }}} # }}}
# {{{ Initialisation method
#
# Initialisation method
def __init__(self, inFilePath, imgFilePath, fontFilePath="DejaVuSansMono.ttf", fontSize=11): def __init__(self, inFilePath, imgFilePath, fontFilePath="DejaVuSansMono.ttf", fontSize=11):
self.inFilePath = inFilePath; self.inFile = open(inFilePath, "r"); self.inFilePath = inFilePath; self.inFile = open(inFilePath, "r");
self.inLines = self.inFile.readlines() self.inLines = self.inFile.readlines()
@ -189,7 +191,6 @@ class MiRCART:
self.outCurX = 0; self.outCurY += 13; self.outCurX = 0; self.outCurY += 13;
self.inFile.close(); self.inFile.close();
self.outImg.save(imgFilePath); self.outImg.save(imgFilePath);
# }}}
# #
# Entry point # Entry point

View File

@ -1,4 +1,4 @@
# mirc2png -- convert ASCII w/ mIRC control codes to monospaced PNG (for EFnet #MiRCART) # mirc2png -- convert ASCII w/ mIRC control codes to monospaced PNG (for EFnet #MiRCART)
* Prerequisites: python3 && python3-pil (&& python3-{json,requests,urllib3} for pngbot.py) on Debian-family Linux distributions * Prerequisites: python3 && python3-pil (&& python3-{json,requests,urllib3} for IrcMiRCARTBot.py) on Debian-family Linux distributions
* mirc2png.py usage: mirc2png.py `<MiRCART input file pathname>` `<PNG image output file pathname>` [`<Font file pathname; defaults to DejaVuSansMono.ttf>`] [`<Font size; defaults to 11>`] * IrcMiRCARTBot.py usage: IrcMiRCARTBot.py `<IRC server hostname>` [`<IRC server port; defaults to 6667>`] [`<IRC bot nick name; defaults to pngbot>`] [`<IRC bot user name; defaults to pngbot>`] [`<IRC bot real name; defaults to pngbot>`] [`<IRC bot channel name; defaults to #MiRCART>`]
* pngbot.py usage: pngbot.py `<IRC server hostname>` [`<IRC server port; defaults to 6667>`] [`<IRC bot nick name; defaults to pngbot>`] [`<IRC bot user name; defaults to pngbot>`] [`<IRC bot real name; defaults to pngbot>`] [`<IRC bot channel name; defaults to #MiRCART>`] * MiRCART.py usage: MiRCART.py `<MiRCART input file pathname>` `<PNG image output file pathname>` [`<Font file pathname; defaults to DejaVuSansMono.ttf>`] [`<Font size; defaults to 11>`]