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