parent
236470190e
commit
98c45040bb
4 changed files with 5 additions and 206 deletions
@ -0,0 +1,3 @@ |
|||||||
|
[submodule "fedbot"] |
||||||
|
path = fedbot |
||||||
|
url = git@git.thj.no:thor/fedbot.git |
@ -1,205 +0,0 @@ |
|||||||
import os |
|
||||||
import sys |
|
||||||
import time |
|
||||||
import copy |
|
||||||
import json |
|
||||||
import pprint |
|
||||||
import threading |
|
||||||
import traceback |
|
||||||
|
|
||||||
from mastodon import Mastodon |
|
||||||
|
|
||||||
def log_obj_str(obj): |
|
||||||
if isinstance(obj, str): |
|
||||||
return obj.strip() |
|
||||||
else: |
|
||||||
return pprint.pformat(obj).strip() |
|
||||||
|
|
||||||
class BotClient: |
|
||||||
DEFAULT_STATE = {"min_status_id": "0"} |
|
||||||
|
|
||||||
def __init__(self, bot, config): |
|
||||||
self.bot = bot |
|
||||||
self.config = { |
|
||||||
"base_url": "https://{}".format(config["name"]), |
|
||||||
"client_file": os.path.join("clients", config["name"], "client.secret"), |
|
||||||
"user_file": os.path.join("clients", config["name"], "user.secret"), |
|
||||||
"state_file": os.path.join("clients", config["name"], "state.json"), |
|
||||||
"cringe_dir": os.path.join("clients", config["name"], "cringe"), |
|
||||||
"based_dir": os.path.join("clients", config["name"], "based"), |
|
||||||
"unsure_dir": os.path.join("clients", config["name"], "unsure"), **config} |
|
||||||
|
|
||||||
self.load_state() |
|
||||||
|
|
||||||
self.poll_thread = threading.Thread( |
|
||||||
target = self.poll_loop, |
|
||||||
name = "{} Poll Loop".format(self.config["name"]), |
|
||||||
args = (), |
|
||||||
kwargs = {}, |
|
||||||
daemon = True) |
|
||||||
|
|
||||||
def log_str(self, obj = "", infix = str()): |
|
||||||
return self.bot.log_str(obj, infix = "{}: {}".format(self.config["name"], infix)) |
|
||||||
|
|
||||||
def log(self, obj = "", infix = str()): |
|
||||||
return self.bot.log(obj, infix = "{}: {}".format(self.config["name"], infix)) |
|
||||||
|
|
||||||
def setup(self): |
|
||||||
client_file_path = os.path.join(os.path.dirname(sys.argv[0]), self.config["client_file"]) |
|
||||||
os.makedirs(os.path.dirname(client_file_path), exist_ok = True) |
|
||||||
if not os.path.exists(client_file_path): |
|
||||||
Mastodon.create_app( |
|
||||||
self.app_name, |
|
||||||
api_base_url = self.config["base_url"], |
|
||||||
to_file = client_file_path) |
|
||||||
|
|
||||||
user_file_path = os.path.join(os.path.dirname(sys.argv[0]), self.config["user_file"]) |
|
||||||
os.makedirs(os.path.dirname(client_file_path), exist_ok = True) |
|
||||||
if not os.path.exists(user_file_path): |
|
||||||
api = Mastodon( |
|
||||||
api_base_url = self.config["base_url"], |
|
||||||
client_id = client_file_path) |
|
||||||
|
|
||||||
auth_url = api.auth_request_url() |
|
||||||
|
|
||||||
self.log("Go to:") |
|
||||||
self.log(auth_url) |
|
||||||
self.log() |
|
||||||
|
|
||||||
auth_code = input(log_string("Enter code: ")) |
|
||||||
|
|
||||||
self.log() |
|
||||||
|
|
||||||
api.log_in(code = auth_code, to_file = user_file_path) |
|
||||||
|
|
||||||
self.api = Mastodon( |
|
||||||
access_token = user_file_path, |
|
||||||
api_base_url = self.config["base_url"]) |
|
||||||
|
|
||||||
def start(self): |
|
||||||
self.poll_thread.start() |
|
||||||
self.on_start() |
|
||||||
|
|
||||||
def poll_loop(self): |
|
||||||
while True: |
|
||||||
try: |
|
||||||
statuses = self.api.timeline(min_id = self.state["min_status_id"]) |
|
||||||
|
|
||||||
if len(statuses) == 0: |
|
||||||
self.on_poll() |
|
||||||
time.sleep(self.config["poll_interval"]) |
|
||||||
else: |
|
||||||
self.on_wake() |
|
||||||
|
|
||||||
while len(statuses) > 0: |
|
||||||
self.on_status_page(statuses) |
|
||||||
|
|
||||||
for status in sorted(statuses, |
|
||||||
key = lambda status: status["created_at"]): |
|
||||||
|
|
||||||
self.on_status(status) |
|
||||||
self.state["min_status_id"] = status["id"] |
|
||||||
|
|
||||||
time.sleep(self.config["rate_limit"]) |
|
||||||
statuses = self.api.fetch_previous(statuses) |
|
||||||
|
|
||||||
self.save_state() |
|
||||||
|
|
||||||
except Exception as exc: |
|
||||||
self.on_poll_exception(exc) |
|
||||||
time.sleep(self.config["retry_rate"]) |
|
||||||
|
|
||||||
def load_state(self): |
|
||||||
self.state = self.on_load_state() |
|
||||||
|
|
||||||
def save_state(self): |
|
||||||
state = self.state.copy() |
|
||||||
self.on_save_state(state) |
|
||||||
|
|
||||||
def on_start(self): |
|
||||||
pass |
|
||||||
|
|
||||||
def on_poll(self): |
|
||||||
pass |
|
||||||
|
|
||||||
def on_poll_exception(self, exc): |
|
||||||
self.log(traceback.format_exc()) |
|
||||||
|
|
||||||
def on_wake(self): |
|
||||||
pass |
|
||||||
|
|
||||||
def on_status_page(self, statuses): |
|
||||||
pass |
|
||||||
|
|
||||||
def on_status(self, status): |
|
||||||
pass |
|
||||||
|
|
||||||
def on_load_state(self): |
|
||||||
state_file_path = os.path.join(os.path.dirname(sys.argv[0]), self.config["state_file"]) |
|
||||||
if os.path.exists(state_file_path): |
|
||||||
os.makedirs(os.path.dirname(state_file_path), exist_ok = True) |
|
||||||
with open(state_file_path) as json_file: |
|
||||||
return json.load(json_file) |
|
||||||
|
|
||||||
return copy.deepcopy(self.DEFAULT_STATE) |
|
||||||
|
|
||||||
def on_save_state(self, state): |
|
||||||
state_file_path = os.path.join(os.path.dirname(sys.argv[0]), self.config["state_file"]) |
|
||||||
os.makedirs(os.path.dirname(state_file_path), exist_ok = True) |
|
||||||
with open(state_file_path, "w") as json_file: |
|
||||||
json.dump(state, json_file, indent = 4) |
|
||||||
|
|
||||||
class Bot: |
|
||||||
DEFAULT_CONFIG = { |
|
||||||
"name": "generic-bot", |
|
||||||
"defaults": { |
|
||||||
"app_name": "Generic Bot", |
|
||||||
"rate_limit": 1, |
|
||||||
"retry_rate": 60, |
|
||||||
"poll_interval": 10 |
|
||||||
}, |
|
||||||
"clients": { |
|
||||||
"mastodon.social": {}}} |
|
||||||
|
|
||||||
def __init__(self, client_type = BotClient, config = {}): |
|
||||||
self.clients = {} |
|
||||||
|
|
||||||
self.client_type = client_type |
|
||||||
|
|
||||||
self.config = {**self.DEFAULT_CONFIG, **config} |
|
||||||
self.config["defaults"] = {**self.DEFAULT_CONFIG["defaults"], **config.get("defaults", {})} |
|
||||||
|
|
||||||
def log_str(self, obj, infix = str()): |
|
||||||
prefix = "{}: {}".format(self.config["name"], infix) |
|
||||||
return prefix + log_obj_str(obj).replace("\n", "\n" + prefix) |
|
||||||
|
|
||||||
def log(self, *args, **kwargs): |
|
||||||
print(self.log_str(*args, **kwargs)) |
|
||||||
|
|
||||||
def start(self): |
|
||||||
self.clients = self.on_start_clients(self.config["clients"]) |
|
||||||
self.on_clients_started(self.clients) |
|
||||||
|
|
||||||
def on_start_clients(self, client_configs): |
|
||||||
clients = {} |
|
||||||
for client_name, client_config in client_configs.items(): |
|
||||||
client_config = { |
|
||||||
**self.config["defaults"], |
|
||||||
**{"name": client_name}, |
|
||||||
**client_config} |
|
||||||
client = self.on_init_client(client_name, client_config) |
|
||||||
client.setup() |
|
||||||
clients[client_config["name"]] = client |
|
||||||
|
|
||||||
start_interval = self.config["defaults"]["poll_interval"] / len(self.config["clients"]) |
|
||||||
for client in clients.values(): |
|
||||||
client.start() |
|
||||||
time.sleep(start_interval) |
|
||||||
|
|
||||||
return clients |
|
||||||
|
|
||||||
def on_init_client(self, client_name, client_config): |
|
||||||
return self.client_type(self, client_config) |
|
||||||
|
|
||||||
def on_clients_started(self, clients): |
|
||||||
pass |
|
@ -0,0 +1 @@ |
|||||||
|
Subproject commit ac336e9492fe8605bcef61775568c0dcbbc78236 |
Loading…
Reference in new issue