summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTianhao Wang <shrik3@riseup.net>2023-03-28 15:11:11 +0200
committerTianhao Wang <shrik3@riseup.net>2023-03-28 15:11:11 +0200
commitb99178fd7b03a7ebd1f65b8b60814a32f13f2333 (patch)
tree0f7aa2afe2aab2d1e19ea885a8e47f48f788cd02
parent7291100dc681fcb640d7f9b6e9cf7239587c5849 (diff)
scan timeline, formatting
-rw-r--r--config.py4
-rw-r--r--main.py93
-rw-r--r--patterns.py10
3 files changed, 81 insertions, 26 deletions
diff --git a/config.py b/config.py
index 0469806..c1e4e7e 100644
--- a/config.py
+++ b/config.py
@@ -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)")
+
diff --git a/main.py b/main.py
index e6bf403..73584f5 100644
--- a/main.py
+++ b/main.py
@@ -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