Initial commit
This commit is contained in:
commit
57b9006a99
|
@ -0,0 +1,5 @@
|
||||||
|
## ctlbot
|
||||||
|
|
||||||
|
Simple bot that scrapes for new messages in the nntpchan's ctl (moderation) channel, and sends them to irc if it finds any.
|
||||||
|
|
||||||
|
It needs to be split across two scripts because twisted is garbage and you can't have multiple reactors at a time
|
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import subprocess
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
import BTEdb
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
user = "CHANGEME"
|
||||||
|
pw = "CHANGEME"
|
||||||
|
from twisted.internet import reactor, protocol, ssl
|
||||||
|
import twisted.protocols.basic
|
||||||
|
|
||||||
|
#debug = True
|
||||||
|
debug = True
|
||||||
|
messages = []
|
||||||
|
|
||||||
|
db = BTEdb.Database("db.json")
|
||||||
|
if not db.TableExists("main"):
|
||||||
|
db.CreateTable("main")
|
||||||
|
db.Insert("main", oldmax=0)
|
||||||
|
oldmax = db.Dump("main")[0]["oldmax"]
|
||||||
|
|
||||||
|
|
||||||
|
def handle_text(s, lines): # handle a text/plain encoded section
|
||||||
|
messages.append(s + " /// " + " ".join(lines[lines.index(""):])) # subject // line1 line2 line3
|
||||||
|
|
||||||
|
|
||||||
|
def handle_part(s, lines): # handle one part of a multipart/mixed encoding
|
||||||
|
types = [k.split()[1][:-1] for k in lines if "Content-Type:" in k]
|
||||||
|
if types[0] == "text/plain":
|
||||||
|
messages.append(
|
||||||
|
s + " /// " + base64.b64decode(" ".join(lines[lines.index(""):])).decode("utf-8").replace("\n", " "))
|
||||||
|
else:
|
||||||
|
print(types[0] + " is not text/plain, skipping")
|
||||||
|
|
||||||
|
|
||||||
|
def handle(lines):
|
||||||
|
if len(lines) == 0:
|
||||||
|
return
|
||||||
|
types = [k.split()[1][:-1] for k in lines if "Content-Type:" in k]
|
||||||
|
subj = [" ".join(k.split()[1:]) for k in lines if "Subject:" in k][0] # grab subject
|
||||||
|
if types[0] == "text/plain": # if it's plain, delegate that
|
||||||
|
handle_text(subj, lines)
|
||||||
|
elif types[0] == "message/rfc822": # if it's rfc822, extract the plain section and delegate that
|
||||||
|
handle_text(subj, lines[lines.index("") + 1:])
|
||||||
|
else: # otherwise split at the boundry and handle each part
|
||||||
|
bound = [k.split()[2].replace("boundary=", "")
|
||||||
|
for k in lines if "multipart/mixed" in k][0]
|
||||||
|
r = [[]]
|
||||||
|
for l in lines:
|
||||||
|
if l == "--" + bound:
|
||||||
|
r.append([])
|
||||||
|
else:
|
||||||
|
r[-1].append(l)
|
||||||
|
for s in r:
|
||||||
|
handle_part(subj, s)
|
||||||
|
|
||||||
|
|
||||||
|
class client(twisted.protocols.basic.LineReceiver):
|
||||||
|
def sl(self, line):
|
||||||
|
if debug:
|
||||||
|
print("Send: " + line)
|
||||||
|
self.sendLine(line.encode("utf-8"))
|
||||||
|
def __init__(self):
|
||||||
|
self.max = 0
|
||||||
|
self.min = 0
|
||||||
|
self.in_message = False
|
||||||
|
self.this_message = []
|
||||||
|
def lineReceived(self, data):
|
||||||
|
data = data.decode("utf-8")
|
||||||
|
if debug:
|
||||||
|
print("Recv: " + data)
|
||||||
|
if len(data) == 0:
|
||||||
|
self.this_message.append("")
|
||||||
|
return
|
||||||
|
elif self.in_message:
|
||||||
|
if data == "." or data.split()[0] == "430":
|
||||||
|
self.in_message = False
|
||||||
|
handle(self.this_message)
|
||||||
|
self.cur += 1
|
||||||
|
if self.cur < self.max:
|
||||||
|
self.sl("ARTICLE " + str(self.cur))
|
||||||
|
self.in_message = True
|
||||||
|
self.this_message = []
|
||||||
|
else:
|
||||||
|
db.Truncate("main")
|
||||||
|
db.Insert("main", oldmax=self.max)
|
||||||
|
self.sl("QUIT")
|
||||||
|
else:
|
||||||
|
self.this_message.append(data)
|
||||||
|
data = data.split()
|
||||||
|
if data[0] == "200": # posting allowed
|
||||||
|
# self.sl("AUTHINFO USER " + user)
|
||||||
|
# if data[0] == "381": # password required
|
||||||
|
# self.sl("AUTHINFO PASS " + pw)
|
||||||
|
# if data[0] == "281": # authentication success
|
||||||
|
self.sl("GROUP ctl")
|
||||||
|
if data[0] == "211": # group stats
|
||||||
|
self.min = int(data[2])
|
||||||
|
self.max = int(data[3])
|
||||||
|
if self.max > oldmax:
|
||||||
|
self.cur = oldmax + 1
|
||||||
|
self.sl("ARTICLE " + str(oldmax + 1))
|
||||||
|
self.in_message = True
|
||||||
|
else:
|
||||||
|
self.sl("QUIT")
|
||||||
|
if data[0] == "205": # bai
|
||||||
|
reactor.stop()
|
||||||
|
# sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
class fac(protocol.ClientFactory):
|
||||||
|
protocol = client
|
||||||
|
|
||||||
|
|
||||||
|
# this connects the protocol to a server running on port 8000
|
||||||
|
reactor.connectTCP("10.8.0.1", 1199, fac())
|
||||||
|
reactor.run()
|
||||||
|
|
||||||
|
if len(messages) == 0:
|
||||||
|
x = "no messages"
|
||||||
|
open("/dev/shm/stream", "w").write(x) # just to make sure if we invoke stream2.py it will die before trying to connect
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
x = json.dumps(messages, indent=4)
|
||||||
|
open("/dev/shm/stream", "w").write(x)
|
||||||
|
subprocess.call(["python3", "stream2.py"])
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
from twisted.words.protocols import irc
|
||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
from twisted.internet import reactor, protocol, ssl
|
||||||
|
import twisted.protocols.basic
|
||||||
|
|
||||||
|
messages = json.load(open("/dev/shm/stream", "r"))
|
||||||
|
#messages = ["TEST!!"]
|
||||||
|
channel = "#nntpchan"
|
||||||
|
|
||||||
|
class ircproto(irc.IRCClient):
|
||||||
|
nickname = "ctlbot"
|
||||||
|
password = "ctlbot/freenode:CHANGEME"
|
||||||
|
|
||||||
|
def sleepAndDie(self): # We need the thread because for some reason calling quit right after we send the message results in the message not getting sent
|
||||||
|
print("Sleeping thread started")
|
||||||
|
time.sleep(4)
|
||||||
|
# self.quit()
|
||||||
|
# reactor.stop()
|
||||||
|
reactor.callFromThread(reactor.stop)
|
||||||
|
|
||||||
|
def signedOn(self):
|
||||||
|
self.join(channel)
|
||||||
|
|
||||||
|
def joined(self, channel_p):
|
||||||
|
if channel == channel_p: # forgetting this check got me in a lot of trouble on freenode
|
||||||
|
for m in messages:
|
||||||
|
print("PRIVMSG " + m)
|
||||||
|
self.msg(channel, "New message to ctl: " + m)
|
||||||
|
t = threading.Thread(target=self.sleepAndDie)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
|
||||||
|
class ircfac(protocol.ReconnectingClientFactory):
|
||||||
|
protocol = ircproto
|
||||||
|
|
||||||
|
reactor.connectSSL("10.8.0.1", 9030, ircfac(), ssl.ClientContextFactory())
|
||||||
|
reactor.run()
|
||||||
|
|
Loading…
Reference in New Issue