From 1bda10bdd9121f3f1e3efc6537f5eb16045044b4 Mon Sep 17 00:00:00 2001 From: Fotoente Date: Sun, 6 Feb 2022 16:28:12 +0100 Subject: [PATCH] Initial release --- .gitignore | 2 + README.md | 32 ++++++- example-miceco.cfg | 5 ++ miceco.py | 208 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 example-miceco.cfg create mode 100644 miceco.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4b6a613 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +emojicount.cfg +miceco.cfg \ No newline at end of file diff --git a/README.md b/README.md index ea0a24d..97e572a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ # MiCECo -Misskey Custom Emoji Counter +**M**isskey **C**ustom **E**moji **Co**unter + +### Introduction +This little script counts custom emojis and used reactions form the day before and notes them automaticaly to Misskey. There is a option if Reactions should be counted as well. + +*Example Note (with counting of Reactions activated)*: https://ente.fun/notes/8wexz5ov1q + +### Installation +Clone the repository into a folder of your choice with `git clone https://github.com/fotoente/MiCECo.git` +Edit the file `example-miceco.cfg` (see table below) and save it as `miceco.cfg` + +You are now ready to run the script with any Python3 Version you like. + +I recommend using a cronjob to let it run on a daily basis. +In your console type `crontab -e` +Add `0 9 * * * python3 /path/to/file/miceco.py > /path/to/file/miceco_output.txt` +The script will now be run every day on 9:00am server time. + +### Options in cfg +|Name|Values|Explanation| +|----|----|----| +|instance|domain.tld|Put here the domain of the Misskey instance you want to read the notes from. Only domain name and TLD, no `/`,`:` or `https` +|user|`username`|The user you want to read the notes from| +|token|`String`|The token from your bot. Needs right to write notes| +|getReaction|`Boolean`|Should reactions of yesterday be counted as well? `True` or `False`| + +### Other notes +The script is written in a way that always the notes and reactions from yesterday(!!!) are caught and counted. There is no option at the moment so you can change the period. +The exact timestamp to get yesterday is determined by the timezone of your server. At the moment there is no way to change the timezone. + +#### Feel free to open a feature request or issue if you want something changed! \ No newline at end of file diff --git a/example-miceco.cfg b/example-miceco.cfg new file mode 100644 index 0000000..bdd06b5 --- /dev/null +++ b/example-miceco.cfg @@ -0,0 +1,5 @@ +[misskey] +instance=//DOMAIN.TLD// +user=//USERNAME// +token=//TOKEN HERE// +getReaction=True \ No newline at end of file diff --git a/miceco.py b/miceco.py new file mode 100644 index 0000000..34960b4 --- /dev/null +++ b/miceco.py @@ -0,0 +1,208 @@ +import sys +import os +import requests +from datetime import * +import json +import configparser + +def check_str_to_bool(text) -> bool: + if (text == "True" or text == "true" or text == "TRUE"): + return True + elif (text == "False" or text == "false" or text == "FALSE"): + return False + else: + return True + +token="" +url="" +noteList = [] +reactionList = [] +reactList=[] +emojiList = [] +emojisTotal = 0 +doubleList = [] +text = "" +getReactions = False + +configfilePath = os.path.join(os.path.dirname(__file__), 'miceco.cfg') + +if (not os.path.exists(configfilePath)): + print("No config File found!") + print("Exit programm!") + sys.exit(1) + +#Load configuration +config = configparser.ConfigParser() +config.read(configfilePath) + +url = "https://" + config.get("misskey","instance") + "/api" +token = config.get("misskey","token") +user = config.get("misskey","user") + +try: + getReactions = check_str_to_bool(config.get("misskey","getReaction")) +except (TypeError, ValueError) as err: + getReactions = False + +try: + req = requests.post(url+"/users/show", json={"username" : user, "host" : None, "i" : token}) + req.raise_for_status() +except requests.exceptions.HTTPError as err: + print("Couldn't get Username!\n"+str(err)) + sys.exit(1) + + +userid = req.json()["id"] +nickname = req.json()["name"] + +heute = date.today() +gestern = heute - timedelta(days = 1) +mitternachtGestern = datetime.combine(gestern, time(0,0,0)) +mitternachtHeute = datetime.combine(heute, time(0,0,0)) + +seit = int(mitternachtGestern.timestamp())*1000 #Javascript uses millisecond timestamp and Python uses float +bis = int(mitternachtHeute.timestamp())*1000 + +lastTimestamp = bis + +while True: + + + if ((bis != lastTimestamp) and (formerTimestamp == lastTimestamp)): + break + + try: + req = requests.post(url+"/users/notes", json = { + "i" : token, + "userId" : userid, + "sinceDate" : seit, + "untilDate" : lastTimestamp, + "includeReplies" : True, + "limit" : 100, + "includeMyRenotes" : False, + "withFiles" : False, + "excludeNsfw" : False + }) + req.raise_for_status() + except requests.exceptions.HTTPError as err: + print("Couldn't get Posts! "+str(err)) + sys.exit(1) + + for jsonObj in req.json(): + #textDict = jsonObj + noteList.append(jsonObj) + + formerTimestamp = lastTimestamp + + lastTime = noteList[len(noteList)-1]["createdAt"] + lastTimestamp = int(datetime.timestamp(datetime.strptime(lastTime, '%Y-%m-%dT%H:%M:%S.%f%z'))*1000) + +for element in noteList: + if (element["text"] == None): #Skip Notes without text + print("Skip Note " + element["id"] + " without Text\nTime noted: " + element["createdAt"]) + continue + + if (element["text"].find(chr(8203))>0): #Skip notes with Zero-Width-Space (Marker to skip older MiCECo notes) + print("Skip Note " + element["id"] + " with Zero-Width-Space\nTime noted: " + element["createdAt"]) + continue + + emojis = element["emojis"] + + if (not emojis): #Notes without emojis will be skipped + continue + + for emoji in emojis: + if (emoji["name"].find("@") == -1): #Only emojis from the own instance, because reactions will be in "emojis" too + if (not emoji["name"] in doubleList): + doubleList.append(emoji["name"]) #Easy way to prevent a double emoji in the list. + dict={"emoji" : emoji["name"], "count" : 0} + emojiList.append(dict) #TODO: Append a dictionary to acces it way easier + + for emoji in emojiList: + emoji["count"] += element["text"].count(emoji["emoji"]) + +doubleList = [] +emojiList = sorted(emojiList, reverse = True , key = lambda d: d["count"]) #Sort it by the most used Emojis! + +if getReactions: + lastTimestamp = bis + + while True: + + if ((bis != lastTimestamp) and (formerTimestamp == lastTimestamp)): + break + + try: + req = requests.post(url+"/users/reactions", json = { + "i" : token, + "userId" : userid, + "sinceDate" : seit, + "untilDate" : lastTimestamp + }) + req.raise_for_status() + except requests.exceptions.HTTPError as err: + print("Couldn't get Posts! "+str(err)) + sys.exit(1) + + for jsonObj in req.json(): + #textDict = jsonObj + reactionList.append(jsonObj) + + formerTimestamp = lastTimestamp + + lastTime = reactionList[len(reactionList)-1]["createdAt"] + lastTimestamp = int(datetime.timestamp(datetime.strptime(lastTime, '%Y-%m-%dT%H:%M:%S.%f%z'))*1000) + + for reaction in reactionList: + react = reaction["type"] + react = react.replace("@.","") + + if (not react in doubleList): + doubleList.append(react) + dict={"reaction" : react, "count" : 0} + reactList.append(dict) + + for reaction in reactList: + if (reaction["reaction"] == react): + reaction["count"] += 1 + + reactList = sorted(reactList, reverse = True , key = lambda d: d["count"]) + + reactionCount = 0 + for react in reactList: + reactionCount += react["count"] + + if (len(reactList) > 0): + reactText="\n\n\nAnd used " + str(reactionCount) + " reactions:\n\n" + chr(9553) + " " + + for reaction in reactList: + reactText += str(reaction["count"]) + "x " + reaction["reaction"] + " " + chr(9553) + " " + else: + reactText="\n\nAnd didn't use any reactions." +else: + reactText="" + +for count in emojiList: + emojisTotal += count["count"] + +if (emojisTotal > 0): + text = nickname + " has written " + str(len(noteList)) + " Notes yesterday, " + gestern.strftime('%a %d-%m-%Y') + "\nand used a total of " + str(emojisTotal) + " Emojis. #miceco" + chr(8203) + "\n\n" + chr(9553) + " " + for element in emojiList: + text += str(element["count"]) + "x\u00A0:" + element["emoji"] + ": " + chr(9553) + " " +else: + text = nickname + " has written " + str(len(noteList)) + " Notes yesterday, " + gestern.strftime('%a %d-%m-%Y') + "\nand didn't used any emojis. #miceco" + chr(8203) + +text += reactText + +#print(text) + +try: + req = requests.post(url+"/notes/create", json = { + "i" : token, + "visibility": "public", + "text": text + }) + req.raise_for_status() +except requests.exceptions.HTTPError as err: + print("Couldn't create Posts! "+str(err)) + sys.exit(1)