diff options
| author | Tianhao Wang <shrik3@riseup.net> | 2023-03-28 15:11:11 +0200 |
|---|---|---|
| committer | Tianhao Wang <shrik3@riseup.net> | 2023-03-28 15:11:11 +0200 |
| commit | b99178fd7b03a7ebd1f65b8b60814a32f13f2333 (patch) | |
| tree | 0f7aa2afe2aab2d1e19ea885a8e47f48f788cd02 | |
| parent | 7291100dc681fcb640d7f9b6e9cf7239587c5849 (diff) | |
scan timeline, formatting
| -rw-r--r-- | config.py | 4 | ||||
| -rw-r--r-- | main.py | 93 | ||||
| -rw-r--r-- | patterns.py | 10 |
3 files changed, 81 insertions, 26 deletions
@@ -8,6 +8,9 @@ TOKEN = 'token.secret' # in seconds POLL_INTERVAL = 20 +# in case of DoS, we won't handle too long a status +MAX_STATUS_LENGTH = 1024 + # account username and PW UNAME = "" PW = "" @@ -21,3 +24,4 @@ except: print("account secret not found, please manually input:") UNAME = input("username or email") PW = input("password (not concealed)") + @@ -9,44 +9,97 @@ from patterns import * def login_refresh_token(): session = Mastodon(client_id=config.CLIENTID) - session.log_in(username=config.UNAME, password=config.PW, to_file=config.TOKEN) + session.log_in(username=config.UNAME, + password=config.PW, to_file=config.TOKEN) return session + def restore_session(): return Mastodon(client_id=config.CLIENTID, access_token=config.TOKEN) class VnilCat: - def __init__(self,config): + def __init__(self, config): self.session = restore_session() + self.tl_lastseen_sid = None + self.config = config try: self.session.account_verify_credentials() except: try: self.session = login_refresh_token() + except: exit() + # try to fetch the lastest status from the timeline + # so that we can skip the ones before we start. + # This is not a persistent state machine, + # we don't know if a older status has been processed + # or not! + try: + tl = self.session.timeline_home(limit=1) + if (len(tl)) != 0: + print("set init status id to begin with...", tl[0]["id"]) + self.tl_lastseen_sid = tl[0]["id"] + except Exception as e: + print("can't init timeline, continue anyways", e) + def not_mine(self, status): + return status['account']['acct'] != self.config.UNAME + + def is_mine(self, status): + return status['account']['acct'] != self.config.UNAME def reply_meow(self, ori_status): print("replying meow to ", ori_status["id"]) - self.session.status_reply(to_status=ori_status, status=random.choice(cat_sounds)) + self.session.status_reply( + to_status=ori_status, status=random.choice(cat_sounds)) + + def handle_command(self, status, content): + print("we have a command", content) + cmd = content[1:].strip().split() + + def handle_home_status(self, status): + self.tl_lastseen_sid = status["id"] + if not self.is_mine(status): + return + acc = status["account"] + content = cleanhtml(status["content"]) + if re_contains_bird.search(content) is not None and len(content) < 10: + print("i see a bird", status["id"]) + try: + s = self.session.status_reply( + to_status=status, status=random.choice(bird_sounds)) + except: + print("fail to post") + def scantimeline(self): + # print("scanning timeline, lastseen=",self.tl_lastseen_sid) + tl = self.session.timeline_home(since_id=self.tl_lastseen_sid) + # important! make sure to iterate from older status to newer + # otherwise the 'last_seen' won't be updated correctly + for status in reversed(tl): + # skip if it's from myself + self.handle_home_status(status=status) # For now we don't handle other interaction types + def handle_mention(self, notification): nid = notification["id"] - # in the case of mention, - # notification['status']['account'] and - # notification['account'] are the same thing acc = notification["account"] status = notification["status"] sid = status["id"] - # created_at = notification["created_at"] - # pleroma = notification["pleroma"] - print("we have a mention > ",nid) - content = status["content"] - if re_contains_meow.search(content) is not None: + print("we have a mention > ", nid) + content = cleanhtml(status["content"]).replace("@cat", "").lstrip() + print(content) + if len(content) == 0 or len(content) > config.MAX_STATUS_LENGTH: + print("invalid status content") + + # prioritized to commands + elif content[0] == "!": + self.handle_command(status, content) + + elif re_contains_meow.search(content) is not None: self.reply_meow(status) print("dismissing notification ", nid) @@ -63,9 +116,6 @@ class VnilCat: print("already following", uid) self.session.notifications_dismiss(nid) - def scan_local(self,session): - print("TODO") - def handle_notification(self): ns = self.session.notifications() for n in ns: @@ -84,19 +134,15 @@ def init_bot(): def run(): - # print("HELLO") cat = VnilCat(config) - # print(cat.session.notifications()) - - # print(session.notifications()) - # session.toot("WTF") - while True: - time.sleep(config.POLL_INTERVAL) try: cat.handle_notification() - except: + cat.scantimeline() + except Exception as e: print("something wrong...") + print(e) + time.sleep(config.POLL_INTERVAL) if __name__ == "__main__": @@ -106,6 +152,3 @@ if __name__ == "__main__": init_bot() run() - - - diff --git a/patterns.py b/patterns.py index 9118227..a91064f 100644 --- a/patterns.py +++ b/patterns.py @@ -2,6 +2,14 @@ import re #### RE PATTERNS #### re_contains_meow = re.compile(r'(me+o+w|喵)',re.IGNORECASE) - +re_contains_bird = re.compile(r'(鸟|bird)',re.IGNORECASE) #### MISC STRING PRESETS #### cat_sounds = ["Meow!", "Mrrrow!", "Purr...", "Meee-OW!", "Mreoww!", "Nya~", "Mew?", "Rowr?", "Prrrr...", "Maow-maow!"] +bird_sounds = ["鸟!", "chirp!"] + +#### PARSE STATUS HTMLs.... +RE_HTML_CLEANR = re.compile('<.*?>') + +def cleanhtml(raw_html): + cleantext = re.sub(RE_HTML_CLEANR, '', raw_html) + return cleantext |
