OppaiBot/oppaibot.py

131 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""
A lazy bot for idling in OppaiTime's announce channel and downloading
every torrent with matching tags.
"""
import os
import re
import functools
import configparser
import http.cookiejar
import requests
from twisted.internet import ssl, protocol, reactor
from twisted.words.protocols import irc
HEADERS = {"User-Agent": "spaghetti is a faggot"}
class OppaiBot(irc.IRCClient):
def __init__(self, config):
self.config = config
self.tracker = self.config.name
self.nickname = self.config["nickname"]
self.username = self.config["ident"]
self.cj = http.cookiejar.MozillaCookieJar(self.config["cookies_txt"])
self.cj.load()
self.watch_dir = self.config["watch_dir"]
self.tags = re.findall(r"\[(.+?)\]", self.config["tags"])
self.tags = [tag.split(",") for tag in self.tags]
self.blacklist = self.config.get("blacklist", "").split(",")
def save_torrent(self, url, directory):
"""
Downloads and saves the torrent file to the given directory.
"""
res = requests.get(url, cookies=self.cj, headers=HEADERS, verify=True)
res.raise_for_status()
if not res.headers['Content-Type'].startswith('application/x-bittorrent'):
print(f"ERROR: Could not download torrent file from URL '{url}'.")
return
fname =re.search(r'filename="(.+)"',res.headers['content-disposition'])
fname = fname.group(1)
fname = fname.encode("latin-1").decode("utf-8")
print(self.tracker + ": Saving torrent:", fname)
with open(os.path.join(directory, fname), "wb") as file:
for chunk in res.iter_content(100000):
file.write(chunk)
def privmsg(self, user, channel, message):
"""
Called when the bot receives a PRIVMSG, which can come from channels
or users alike.
"""
# More advanced logic is left as an exercise to the reader.
black_in_msg = [tag in message for tag in self.blacklist if tag]
if any(black_in_msg):
return
tags_in_msg = []
for tag_group in self.tags:
tags_in_group = []
for tag in tag_group:
if tag.startswith("!"):
tags_in_group.append(tag[1:] not in message)
else:
tags_in_group.append(tag in message)
tags_in_msg.append(all(tags_in_group))
if any(tags_in_msg):
url = re.findall(r"(http.+?) ", message)[1]
self.save_torrent(url, self.watch_dir)
def joined(self, channel):
"""Called when the bot joins a new channel."""
print(self.tracker + ": Joined", channel)
def signedOn(self):
"""Called when the bot successfully connects to the server."""
print(self.tracker + ": Signed on as", self.nickname)
self.mode(self.nickname, True, "B") # set +B on self
self.msg(self.config["tracker_bot"], self.config["auth_msg"])
def nickChanged(self, nick):
"""Called when my nick has been changed."""
print(self.tracker + ": Nick changed to", nick)
class OppaiBotFactory(protocol.ReconnectingClientFactory):
# black magic going on here
protocol = property(lambda s: functools.partial(OppaiBot, s.config))
def __init__(self, config):
self.config = config
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(
description="Downloads new torrents from a torrent tracker's IRC " \
+ "announce channel.")
parser.add_argument(
"-c",
"--config",
default="config.cfg",
help="Specify an alternate config file to use.")
args = parser.parse_args()
config = configparser.ConfigParser()
config.read(args.config)
for tracker in config.sections():
tracker_config = config[tracker]
server = tracker_config["server"]
port = tracker_config.getint("port")
print("Connecting to:", tracker_config.name)
reactor.connectSSL(
server,
port,
OppaiBotFactory(tracker_config),
ssl.ClientContextFactory())
reactor.run()