Initial commit

This commit is contained in:
Niles Rogoff 2017-04-02 19:14:31 -04:00
commit 57b9006a99
No known key found for this signature in database
GPG Key ID: B78B908F23430F80
3 changed files with 176 additions and 0 deletions

5
readme.md Normal file
View File

@ -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

130
stream.py Normal file
View File

@ -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"])

41
stream2.py Normal file
View File

@ -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()