2021-05-23 16:33:21 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import subprocess, re, os, inspect, logging
|
|
|
|
|
|
|
|
class Bot:
|
2021-05-23 19:17:50 +00:00
|
|
|
def __init__(self, cmd, initcmd = [], user=None):
|
2021-05-23 16:33:21 +00:00
|
|
|
|
|
|
|
if user: os.environ["USER"] = user
|
2021-06-23 19:36:44 +00:00
|
|
|
if os.environ["IRCSERVER"]: del os.environ["IRCSERVER"]
|
2021-05-23 16:33:21 +00:00
|
|
|
|
|
|
|
self.proc = subprocess.Popen(cmd,
|
|
|
|
stdin = subprocess.PIPE,
|
|
|
|
stdout = subprocess.PIPE,
|
2021-05-23 19:17:50 +00:00
|
|
|
stderr = open("/dev/null", mode="w"))
|
|
|
|
|
2021-05-23 19:55:43 +00:00
|
|
|
for u in initcmd: self.write(u)
|
2021-05-23 16:33:21 +00:00
|
|
|
for u in dir(self):
|
|
|
|
if u[:3] == "on_":
|
|
|
|
self.write('/on ^' + u[3:]
|
|
|
|
+ ' * { echo trigger_event.' + u + ' $* }')
|
|
|
|
|
2021-05-24 09:15:01 +00:00
|
|
|
r = re.compile("trigger_event\.(on_[^ ]+)")
|
2021-05-23 16:33:21 +00:00
|
|
|
for line in self.proc.stdout:
|
|
|
|
try:
|
|
|
|
line = line.decode('utf-8')
|
|
|
|
u = r.match(line)
|
|
|
|
if u:
|
|
|
|
m = getattr(self, u.groups()[0])
|
|
|
|
args = re.split(" +", line.rstrip(),
|
2021-05-24 10:20:43 +00:00
|
|
|
maxsplit = len(inspect.signature(m).parameters))
|
2021-05-23 16:33:21 +00:00
|
|
|
args.pop(0)
|
|
|
|
m(*args)
|
|
|
|
except Exception as e:
|
|
|
|
logging.error("[" + type(e).__name__ + "] " + str(e))
|
|
|
|
|
|
|
|
def write(self, msg):
|
|
|
|
self.proc.stdin.write(bytes(msg + "\n", "utf-8"))
|
|
|
|
self.proc.stdin.flush()
|
|
|
|
|
|
|
|
|
2021-05-24 09:15:01 +00:00
|
|
|
logging.basicConfig(format='%(asctime)s %(message)s',
|
|
|
|
level=logging.INFO)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-05-24 09:22:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################
|
2021-05-24 19:50:57 +00:00
|
|
|
# Various extended classes #
|
2021-05-24 09:22:21 +00:00
|
|
|
############################
|
|
|
|
|
|
|
|
|
2021-05-24 19:50:57 +00:00
|
|
|
# This is the main extended class, which should be used as a starting
|
|
|
|
# point for implementing bots (if you need to fire delayed callbacks,
|
|
|
|
# use the ExtenddThreadedBot instead).
|
2021-05-24 09:15:01 +00:00
|
|
|
class ExtendedBot(Bot):
|
|
|
|
"""
|
|
|
|
An extended class logging all 'general_notice' and 'connect' info.
|
|
|
|
It should be used rather than the low-level bot because it will help
|
|
|
|
locating various issues (unless you want to implement these methods
|
|
|
|
by yourself for other purposes).
|
|
|
|
"""
|
|
|
|
def on_general_notice(self, server, first_word, msg):
|
|
|
|
logging.info("[%s] -%s- %s" % (server, first_word, msg))
|
|
|
|
def on_connect(self, server, port, host):
|
|
|
|
logging.info("Connected to %s (port %s) at host %s"
|
|
|
|
% (server, port, host))
|
|
|
|
|
|
|
|
|
2021-05-24 19:50:57 +00:00
|
|
|
# If you don't need delayed callbacks, you can safely remove the lines
|
|
|
|
# below (including the 'import threading' one) and use the ExtendedBot
|
|
|
|
# instead.
|
2021-05-24 19:39:16 +00:00
|
|
|
import threading
|
|
|
|
class ExtendedThreadedBot(ExtendedBot):
|
|
|
|
"""
|
|
|
|
A child class of ExtendedBot adding enough threading support for
|
|
|
|
allowing a self.delay method for a callback function.
|
|
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.S_write = threading.Semaphore()
|
|
|
|
super(ExtendedThreadedBot, self).__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
def write(self, msg):
|
|
|
|
"""
|
|
|
|
Protected version of self.write
|
|
|
|
(protected against multithreading simultaneous calls)
|
|
|
|
"""
|
|
|
|
self.S_write.acquire()
|
2021-05-24 19:46:47 +00:00
|
|
|
super(ExtendedThreadedBot, self).write(msg)
|
2021-05-24 19:39:16 +00:00
|
|
|
self.S_write.release()
|
|
|
|
|
|
|
|
def delay(self, time, callback):
|
|
|
|
"""
|
|
|
|
Set a callback function allowed to call self.write
|
|
|
|
(which is protected by a Semaphore)
|
|
|
|
"""
|
|
|
|
threading.Timer(time, callback).start()
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-05-24 19:50:57 +00:00
|
|
|
#########################
|
|
|
|
# Various examples bots #
|
|
|
|
#########################
|
2021-05-24 19:39:16 +00:00
|
|
|
|
|
|
|
|
2021-05-23 16:33:21 +00:00
|
|
|
# This bot does nothing except replying to private messages
|
2021-05-24 09:15:01 +00:00
|
|
|
class Test1(ExtendedBot):
|
2021-05-23 16:33:21 +00:00
|
|
|
def on_msg(self, nick, msg):
|
|
|
|
self.write("/msg " + nick + " Hi, " + nick + "!")
|
|
|
|
|
2021-05-24 19:39:16 +00:00
|
|
|
|
2021-05-23 16:33:21 +00:00
|
|
|
# This bot writes "I like Shakespeare" whenever someone says
|
|
|
|
# something about Shakespare in the current channel; it also
|
2021-05-23 19:55:43 +00:00
|
|
|
# quits IRC if someone sends a private message starting with
|
2021-05-23 16:33:21 +00:00
|
|
|
# the two words: Just leave!
|
2021-05-24 09:15:01 +00:00
|
|
|
class Test2(ExtendedBot):
|
2021-05-23 16:33:21 +00:00
|
|
|
def on_msg(self, nick, msg):
|
2021-05-23 19:55:43 +00:00
|
|
|
if msg[:11].lower() == "just leave!":
|
2021-05-23 16:33:21 +00:00
|
|
|
self.write("/quit I leave!")
|
|
|
|
def on_public(self, nick, channel, msg):
|
|
|
|
if "shakespeare" in msg.lower():
|
|
|
|
self.write("/say I like Shakespeare!")
|
|
|
|
|
2021-05-24 19:39:16 +00:00
|
|
|
|
|
|
|
# This bot uses the 'delay' feature from ExtendedThreadedBot
|
|
|
|
class Test3(ExtendedThreadedBot):
|
|
|
|
def on_msg(self, nick, msg):
|
|
|
|
if msg[:11].lower() == "just leave!":
|
|
|
|
self.write("/quit I leave!")
|
|
|
|
def on_public(self, nick, channel, msg):
|
|
|
|
msg = msg.lower()
|
|
|
|
if "shakespeare" in msg:
|
|
|
|
self.write("/say I like Shakespeare!")
|
|
|
|
elif "dickens" in msg:
|
|
|
|
self.delay(5,
|
|
|
|
lambda: self.write("/say I also like Dickens!"))
|
2021-05-23 16:33:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-05-24 07:11:55 +00:00
|
|
|
# Choose one bot above and run it:
|
2021-05-24 07:02:25 +00:00
|
|
|
|
|
|
|
## Running an Epic5 bot with SSL
|
|
|
|
## =============================
|
2021-05-24 19:39:16 +00:00
|
|
|
Test3([ "/home/pi/.local/bin/epic5",
|
2021-05-24 07:11:55 +00:00
|
|
|
"-d", "-q", "-c", "#test",
|
|
|
|
"-n", "TestNick",
|
|
|
|
"irc.efnet.org:6697::::type=IRC-SSL" ],
|
|
|
|
user="testus") # export the USER environment variable
|
2021-05-23 19:09:17 +00:00
|
|
|
|
2021-05-24 07:02:25 +00:00
|
|
|
## Running an ircII bot with SSL
|
|
|
|
## =============================
|
2021-05-24 07:11:55 +00:00
|
|
|
## Test2([ "ircII",
|
|
|
|
## "-d", "-q", "TestNick", "-c", "#test",
|
|
|
|
## "SSLIRC/irc.freenode.net:6697" ],
|
|
|
|
## initcmd = ["/set novice off"],
|
|
|
|
## user = "TestWarrior") # export the USER environment variable
|
2021-05-23 19:55:43 +00:00
|
|
|
|
2021-05-24 07:02:25 +00:00
|
|
|
## Running a BitchX bot without SSL
|
|
|
|
## ================================
|
|
|
|
## Test2([ "BitchX", # No SSL
|
|
|
|
## "-d", "-q", "TestNick", "-c", "#test",
|
|
|
|
## "irc.freenode.net" ],
|
|
|
|
## user = "TestWarrior") # export the USER environment variable
|
2021-05-23 19:55:43 +00:00
|
|
|
|
|
|
|
|
2021-05-24 07:02:25 +00:00
|
|
|
## Running a ScrollZ bot without SSL
|
|
|
|
## =================================
|
|
|
|
## Test2([ "scrollz", # No SSL
|
|
|
|
## "-d", "-q", "TestNick", "-c", "#test",
|
|
|
|
## "irc.freenode.net" ],
|
|
|
|
## user = "TestWarrior") # export the USER environment variable
|