Merge remote-tracking branch 'origin/main'

This commit is contained in:
Fotoente 2022-02-20 09:50:15 +01:00
commit d6763d4c6b
2 changed files with 102 additions and 107 deletions

View file

@ -1,29 +1,21 @@
from datetime import datetime
import os
import asyncio import asyncio
from random import *
import mi
import sys
import configparser
import threading import threading
from pathlib import Path
from mi import Note
from mi.ext import commands, tasks from mi.ext import commands, tasks
from mi.note import Note from mi.framework import Note
from mi.router import Router from mi.framework.router import Router
from roboduck import * from roboduck import *
# Load Misskey configuration
#Load Misskey configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read((Path(__file__).parent).joinpath('bot.cfg')) config.read(Path(__file__).parent.joinpath('bot.cfg'))
uri="wss://"+config.get("misskey","instance_write")+"/streaming" uri = config.get("misskey", "instance_write")
token=config.get("misskey","token") token = config.get("misskey", "token")
class MyBot(commands.Bot): class MyBot(commands.Bot):
text_model = None #Holds the markov object, so it won't be recreated everytime text_model = None # Holds the markov object, so it won't be recreated everytime
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@ -35,36 +27,30 @@ class MyBot(commands.Bot):
@tasks.loop(43200) @tasks.loop(43200)
async def loop_12h(self): async def loop_12h(self):
thread_update = threading.Thread(target=update) thread_update = threading.Thread(target=update)
thread_update.setDaemon(True) thread_update.setDaemon(True)
thread_update.start() thread_update.start()
async def on_ready(self, ws): async def on_ready(self, ws):
await Router(ws).connect_channel(["global", "main"]) #Connect to global and main channels await Router(ws).connect_channel(["global", "main"]) # Connect to global and main channels
await bot.client.note.send(content=datetime.now().strftime('%Y-%m-%d %H:%M:%S') + " :roboduck: Bot started!", visibility="specified") await bot.client.note.send(content=datetime.now().strftime('%Y-%m-%d %H:%M:%S') + " :roboduck: Bot started!",
self.loop_12h.start() #Launching renew posts every 12 hours visibility="specified")
self.loop_1h.start() # self.loop_12h.start() # Launching renew posts every 12 hours
print(datetime.now().strftime('%Y-%m-%d %H:%M:%S')+" Roboduck Bot started!") self.loop_1h.start() #
print(datetime.now().strftime('%Y-%m-%d %H:%M:%S') + " Roboduck Bot started!")
async def on_mention(self, note: Note): async def on_mention(self, note: Note):
text="" if not note.author.is_bot:
if (not note.author.is_bot): text = note.author.action.get_mention()
inhalt=note.content
if (note.author.host is None):
text = "@" + note.author.name + " " #Building the reply on same instance
else:
text = "@" + note.author.name + "@" + note.author.host + " " #Building the reply on foreign instance
text += create_sentence() text += create_sentence()
await note.reply(content=text) #Reply to a note await note.reply(content=text) # Reply to a note
if __name__ == "__main__": if __name__ == "__main__":
databasepath = (Path(__file__).parent).joinpath('roboduck.db') databasepath = Path(__file__).parent.joinpath('roboduck.db')
if (not (os.path.exists(databasepath) and os.stat(databasepath).st_size != 0)): if not (os.path.exists(databasepath) and os.stat(databasepath).st_size != 0):
init_bot() init_bot()
bot = MyBot() bot = MyBot()

View file

@ -10,6 +10,7 @@ from pathlib import Path
from datetime import * from datetime import *
from time import sleep from time import sleep
def check_str_to_bool(text) -> bool: def check_str_to_bool(text) -> bool:
if (text == "True" or text == "true" or text == "TRUE"): if (text == "True" or text == "true" or text == "TRUE"):
return True return True
@ -18,6 +19,7 @@ def check_str_to_bool(text) -> bool:
else: else:
return True return True
def get_notes(**kwargs): def get_notes(**kwargs):
noteid = "k" noteid = "k"
sinceid = "" sinceid = ""
@ -27,12 +29,12 @@ def get_notes(**kwargs):
if (kwargs): if (kwargs):
if ("min_notes" in kwargs): if ("min_notes" in kwargs):
#print("min_notes found!") # print("min_notes found!")
init = True init = True
min_notes = kwargs["min_notes"] min_notes = kwargs["min_notes"]
elif ("lastnote" in kwargs): elif ("lastnote" in kwargs):
#print("Lastnote found!") # print("Lastnote found!")
init = False init = False
sinceid = kwargs["lastnote"] sinceid = kwargs["lastnote"]
@ -45,41 +47,40 @@ def get_notes(**kwargs):
print("Exiting routine") print("Exiting routine")
return None return None
#Load configuration # Load configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(os.path.join(os.path.dirname(__file__), 'bot.cfg')) config.read(os.path.join(os.path.dirname(__file__), 'bot.cfg'))
#print(os.path.join(os.path.dirname(__file__), 'bot.cfg')) # print(os.path.join(os.path.dirname(__file__), 'bot.cfg'))
url="https://"+config.get("misskey","instance_read")+"/api/users/show" url = "https://" + config.get("misskey", "instance_read") + "/api/users/show"
host=config.get("misskey","instance_read") host = config.get("misskey", "instance_read")
try: try:
req = requests.post(url, json={"username" : config.get("misskey","user_read"), "host" : host}) req = requests.post(url, json={"username": config.get("misskey", "user_read"), "host": host})
req.raise_for_status() req.raise_for_status()
except requests.exceptions.HTTPError as err: except requests.exceptions.HTTPError as err:
print("Couldn't get Username! " + str(err)) print("Couldn't get Username! " + str(err))
sys.exit(1) sys.exit(1)
userid = req.json()["id"] userid = req.json()["id"]
#Read & Sanitize Inputs from Config File # Read & Sanitize Inputs from Config File
try: try:
includeReplies = check_str_to_bool(config.get("markov","includeReplies")) includeReplies = check_str_to_bool(config.get("markov", "includeReplies"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
includeReplies = True includeReplies = True
try: try:
includeMyRenotes = check_str_to_bool(config.get("markov","includeMyRenotes")) includeMyRenotes = check_str_to_bool(config.get("markov", "includeMyRenotes"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
includeMyRenotes = False includeMyRenotes = False
try: try:
excludeNsfw = check_str_to_bool(config.get("markov","excludeNsfw")) excludeNsfw = check_str_to_bool(config.get("markov", "excludeNsfw"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
excludeNsfw = True excludeNsfw = True
run = True run = True
oldnote="" oldnote = ""
while run: while run:
@ -87,19 +88,19 @@ def get_notes(**kwargs):
break break
try: try:
req = requests.post("https://"+config.get("misskey","instance_read")+"/api/users/notes", json = { req = requests.post("https://" + config.get("misskey", "instance_read") + "/api/users/notes", json={
"userId": userid, "userId": userid,
"includeReplies" : includeReplies, "includeReplies": includeReplies,
"limit" : 100, "limit": 100,
"includeMyRenotes" : includeMyRenotes, "includeMyRenotes": includeMyRenotes,
"withFiles" : False, "withFiles": False,
"excludeNsfw" : excludeNsfw, "excludeNsfw": excludeNsfw,
"untilId" : noteid, "untilId": noteid,
"sinceId" : sinceid "sinceId": sinceid
}) })
req.raise_for_status() req.raise_for_status()
except requests.exceptions.HTTPError as err: except requests.exceptions.HTTPError as err:
print("Couldn't get Posts! "+str(err)) print("Couldn't get Posts! " + str(err))
sys.exit(1) sys.exit(1)
for jsonObj in req.json(): for jsonObj in req.json():
@ -110,36 +111,38 @@ def get_notes(**kwargs):
oldnote = noteid oldnote = noteid
noteid = notesList[len(notesList)-1]["id"] noteid = notesList[len(notesList) - 1]["id"]
print(str(len(notesList)) + " Notes read.") print(str(len(notesList)) + " Notes read.")
print("Processing notes...") print("Processing notes...")
for element in notesList: for element in notesList:
lastTime = element["createdAt"] lastTime = element["createdAt"]
lastTimestamp = int(datetime.timestamp(datetime.strptime(lastTime, '%Y-%m-%dT%H:%M:%S.%f%z'))*1000) lastTimestamp = int(datetime.timestamp(datetime.strptime(lastTime, '%Y-%m-%dT%H:%M:%S.%f%z')) * 1000)
content = element["text"] content = element["text"]
if content is None: #Skips empty notes (I don't know how there could be empty notes) if content is None: # Skips empty notes (I don't know how there could be empty notes)
continue continue
content = regex.sub(r"(?>@(?>[\w\-])+)(?>@(?>[\w\-\.])+)?", '', content) #Remove instance name with regular expression content = regex.sub(r"(?>@(?>[\w\-])+)(?>@(?>[\w\-\.])+)?", '',
content = content.replace("::",": :") #Break long emoji chains content) # Remove instance name with regular expression
content = content.replace("@", "@"+chr(8203)) content = content.replace("::", ": :") # Break long emoji chains
content = content.replace("@", "@" + chr(8203))
dict = {"id" : element["id"], "text" : content, "timestamp" : lastTimestamp} dict = {"id": element["id"], "text": content, "timestamp": lastTimestamp}
returnList.append(dict) returnList.append(dict)
return returnList return returnList
def calculate_markov_chain(): def calculate_markov_chain():
text = "" text = ""
#Load configuration # Load configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read((Path(__file__).parent).joinpath('bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
try: try:
max_notes = config.get("markov","max_notes") max_notes = config.get("markov", "max_notes")
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
max_notes = "10000" max_notes = "10000"
@ -161,13 +164,14 @@ def calculate_markov_chain():
text += row[0] + "\n" text += row[0] + "\n"
markovchain = markovify.Text(text) markovchain = markovify.Text(text)
markovchain.compile(inplace = True) markovchain.compile(inplace=True)
markov_json = markovchain.to_json() markov_json = markovchain.to_json()
with open((Path(__file__).parent).joinpath('markov.json'), "w", encoding="utf-8") as markov: with open((Path(__file__).parent).joinpath('markov.json'), "w", encoding="utf-8") as markov:
json.dump(markov_json, markov) json.dump(markov_json, markov)
def clean_database(): def clean_database():
databasepath = (Path(__file__).parent).joinpath('roboduck.db') databasepath = (Path(__file__).parent).joinpath('roboduck.db')
if (not (os.path.exists(databasepath) and os.stat(databasepath).st_size != 0)): if (not (os.path.exists(databasepath) and os.stat(databasepath).st_size != 0)):
@ -178,12 +182,12 @@ def clean_database():
with open(databasepath, "a", encoding="utf-8") as f: with open(databasepath, "a", encoding="utf-8") as f:
database = sqlite3.connect(databasepath) database = sqlite3.connect(databasepath)
#Reading config file bot.cfg with config parser # Reading config file bot.cfg with config parser
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read((Path(__file__).parent).joinpath('bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
#print((Path(__file__).parent).joinpath('bot.cfg')) # print((Path(__file__).parent).joinpath('bot.cfg'))
try: try:
max_notes = config.get("markov","max_notes") max_notes = config.get("markov", "max_notes")
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
max_notes = "10000" max_notes = "10000"
@ -193,59 +197,60 @@ def clean_database():
database.commit() database.commit()
database.close() database.close()
def create_sentence(): def create_sentence():
with open((os.path.join((Path(__file__).parent), 'markov.json')), "r", encoding="utf-8") as markov: with open((os.path.join((Path(__file__).parent), 'markov.json')), "r", encoding="utf-8") as markov:
markov_json = json.load(markov) markov_json = json.load(markov)
text_model = markovify.Text.from_json(markov_json) text_model = markovify.Text.from_json(markov_json)
note="" note = ""
#Reading config file bot.cfg with config parser # Reading config file bot.cfg with config parser
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read((Path(__file__).parent).joinpath('bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
#print((Path(__file__).parent).joinpath('bot.cfg')) # print((Path(__file__).parent).joinpath('bot.cfg'))
#Read & Sanitize Inputs # Read & Sanitize Inputs
try: try:
test_output = check_str_to_bool(config.get("markov","test_output")) test_output = check_str_to_bool(config.get("markov", "test_output"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print("test_output: " + str(err)) # print("test_output: " + str(err))
test_output = True test_output = True
if (test_output): if (test_output):
try: try:
tries = int(config.get("markov","tries")) tries = int(config.get("markov", "tries"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print("tries: " + str(err)) # print("tries: " + str(err))
tries = 250 tries = 250
try: try:
max_overlap_ratio = float(config.get("markov","max_overlap_ratio")) max_overlap_ratio = float(config.get("markov", "max_overlap_ratio"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print("max_overlap_ratio: " + str(err)) # print("max_overlap_ratio: " + str(err))
max_overlap_ratio = 0.7 max_overlap_ratio = 0.7
try: try:
max_overlap_total = int(config.get("markov","max_overlap_total")) max_overlap_total = int(config.get("markov", "max_overlap_total"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print("max_overlap_total: " + str(err)) # print("max_overlap_total: " + str(err))
max_overlap_total = 10 max_overlap_total = 10
try: try:
max_words = int(config.get("markov","max_words")) max_words = int(config.get("markov", "max_words"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print("max_words: " + str(err)) # print("max_words: " + str(err))
max_words = None max_words = None
try: try:
min_words = int(config.get("markov","min_words")) min_words = int(config.get("markov", "min_words"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print("min_words: " + str(err)) # print("min_words: " + str(err))
min_words = None min_words = None
if (max_words is not None and min_words is not None): if (max_words is not None and min_words is not None):
if (min_words >= max_words): if (min_words >= max_words):
#print("min_words ("+str(min_words)+") bigger than max_words ("+str(max_words)+")! Swapping values!") # print("min_words ("+str(min_words)+") bigger than max_words ("+str(max_words)+")! Swapping values!")
swap = min_words swap = min_words
min_words = max_words min_words = max_words
max_words = swap max_words = swap
@ -268,20 +273,21 @@ def create_sentence():
print("min_words: " + str(min_words)) print("min_words: " + str(min_words))
""" """
#Applying Inputs # Applying Inputs
note = text_model.make_sentence( note = text_model.make_sentence(
test_output = test_output, test_output=test_output,
tries = tries, tries=tries,
max_overlap_ratio = max_overlap_ratio, max_overlap_ratio=max_overlap_ratio,
max_overlap_total = max_overlap_total, max_overlap_total=max_overlap_total,
max_words = max_words, max_words=max_words,
min_words = min_words min_words=min_words
) )
if (note is not None): if (note is not None):
return note return note
else: else:
return "Error in markov chain sentence creation: Couldn't calculate sentence!\n\n☹ Please try again! " return "Error in markov chain sentence creation: Couldn't calculate sentence!\n\n☹ Please try again! "
def update(): def update():
notesList = [] notesList = []
databasepath = (Path(__file__).parent).joinpath('roboduck.db') databasepath = (Path(__file__).parent).joinpath('roboduck.db')
@ -299,7 +305,7 @@ def update():
sinceNote = data.fetchone()[0] sinceNote = data.fetchone()[0]
notesList = get_notes(lastnote = sinceNote) notesList = get_notes(lastnote=sinceNote)
if (notesList == 0): if (notesList == 0):
database.close() database.close()
@ -307,7 +313,8 @@ def update():
print("Insert new notes to database...") print("Insert new notes to database...")
for note in notesList: for note in notesList:
database.execute("INSERT OR IGNORE INTO notes (id, text, timestamp) VALUES(?, ?, ?)", [note["id"], note["text"], note["timestamp"]]) database.execute("INSERT OR IGNORE INTO notes (id, text, timestamp) VALUES(?, ?, ?)",
[note["id"], note["text"], note["timestamp"]])
database.commit() database.commit()
print("Notes updated!") print("Notes updated!")
@ -326,6 +333,7 @@ def update():
print("\nUpdate done!") print("\nUpdate done!")
def init_bot(): def init_bot():
notesList = [] notesList = []
databasepath = (Path(__file__).parent).joinpath('roboduck.db') databasepath = (Path(__file__).parent).joinpath('roboduck.db')
@ -345,23 +353,24 @@ def init_bot():
print("Table NOTES created...") print("Table NOTES created...")
#Load configuration # Load configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read((Path(__file__).parent).joinpath('bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
try: try:
initnotes = int(config.get("markov","min_notes")) initnotes = int(config.get("markov", "min_notes"))
except (TypeError, ValueError) as err: except (TypeError, ValueError) as err:
#print(err) # print(err)
initnotes=1000 initnotes = 1000
print("Try reading first " + str(initnotes) + " notes.") print("Try reading first " + str(initnotes) + " notes.")
notesList = get_notes(min_notes = initnotes) notesList = get_notes(min_notes=initnotes)
print("Writing notes into database...") print("Writing notes into database...")
for note in notesList: for note in notesList:
database.execute("INSERT INTO notes (id, text, timestamp) VALUES(?, ?, ?)", [note["id"], note["text"], note["timestamp"]]) database.execute("INSERT INTO notes (id, text, timestamp) VALUES(?, ?, ?)",
[note["id"], note["text"], note["timestamp"]])
database.commit() database.commit()
database.close() database.close()