Merge pull request #8 from fotoente/dev

Dev
This commit is contained in:
fotoente 2022-02-19 16:05:07 +01:00 committed by GitHub
commit c77a99fe5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 201 additions and 138 deletions

9
.dockerignore Normal file
View file

@ -0,0 +1,9 @@
.git*
__pycache__
bot.cfg
README.md
LICENSE
markov.json
roboduck.db
docker-compose.yml
Dockerfile

26
.github/workflows/dockerhub.yml vendored Normal file
View file

@ -0,0 +1,26 @@
name: dockerhub
on:
push:
branches:
- main
jobs:
buildx:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/misskey-ebooks-bot:latest
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6

11
Dockerfile Normal file
View file

@ -0,0 +1,11 @@
FROM python:3
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install git+https://github.com/yupix/Mi.py.git@v3.3.0
COPY . .
CMD [ "python", "-u", "./rdbot.py" ]

View file

@ -3,19 +3,21 @@ Misskey eBooks Bot with Markov Chain
[Example @roboduck@ente.fun](https://ente.fun/@roboduck) [Example @roboduck@ente.fun](https://ente.fun/@roboduck)
### Introduction ## Introduction
This small python script is a Markov Chain eBooks bot based on the framework of [mi.py](https://github.com/yupix/Mi.py.git) This small python script is a Markov Chain eBooks bot based on the framework of [mi.py](https://github.com/yupix/Mi.py.git)
It can only read and write from and to Misskey. Reading from Mastodon or Pleroma is not (yet) implemented. It can only read and write from and to Misskey. Reading from Mastodon or Pleroma is not (yet) implemented.
It posts every hour on his own and reacts to mentions. Every 12 hours the bot reloads the notes and recalculates the Markov Chain. It posts every hour on his own and reacts to mentions. Every 12 hours the bot reloads the notes and recalculates the Markov Chain.
### Operating mode ## Operating mode
On the first start up the bot loads a given number of posts into his database and calculates the Markov Chain out of it. On the first start up the bot loads a given number of posts into his database and calculates the Markov Chain out of it.
After this he only updates the database with new posts. The upgrading is threaded so the bot itself isn't interrupted while the new markov chain is calulated. After this he only updates the database with new posts. The upgrading is threaded so the bot itself isn't interrupted while the new markov chain is calulated.
### Installation ## Installation
To run `mi.py` you must isntall `python3.9` and `python3.9-dev` onto your system. (Please be aware of the requirements for mi.py!)
### Host Installation
To run `mi.py` you must install `python3.9` and `python3.9-dev` onto your system. (Please be aware of the requirements for mi.py!)
`mi.py` is still under development and a lot of things change there quickly so please be aware that there could be chances that something changed, that I haven't implemented in the bot at the moment. `mi.py` is still under development and a lot of things change there quickly so please be aware that there could be chances that something changed, that I haven't implemented in the bot at the moment.
to install `mi.py`please use the following command. to install `mi.py`please use the following command.
`pip install git+https://github.com/yupix/Mi.py.git` `pip install git+https://github.com/yupix/Mi.py.git`
@ -28,9 +30,26 @@ configparser
or just use the command `pip install -r requirements.txt` in the local folder where you cloned the repo. or just use the command `pip install -r requirements.txt` in the local folder where you cloned the repo.
Before starting the bot, please copy `example-bot.cfg` to `bot.cfg` and
configure it according to the configuration section below.
### Configuration The best way to run it would be a `systemd` unit file and run it as a deamon.
To run the bot please edit `example-bot.cfg` and rename it to `bot.cfg` Just to test it you can use `nohup python3.9 rdbot.py &` in the directory the bot is located in.
### Docker
To host this image with docker, copy the `docker-compose.yml` file to the directory that you want to host it from.
Next, you'll need to copy the contents of `example-bot.cfg` to `bot.cfg` in the
same directory and configure it according to the configuration section below.
Run `touch markov.json roboduck.db` in order to create the markov and database
files before starting the docker container. These files must already exist
before starting the docker container.
Then, simply run `docker-compose up` to start the app, or `docker-compose up -d`
to start the bot in detached mode!
## Configuration
Following things can be edited: Following things can be edited:
|Name|Values|Explanation| |Name|Values|Explanation|
|----|----|----| |----|----|----|
@ -52,12 +71,6 @@ Following things can be edited:
You can change the configuration while the bot is running. No restart necessary, they take immediate effect. You can change the configuration while the bot is running. No restart necessary, they take immediate effect.
### Starting the Bot ## Known Quirks
The best way to run it would be a `systemd` unit file and run it as a deamon.
Just to test it you can use `nohup python3.9 rdbot.py &` in the directory the bot is located in.
### Known Quirks
- The startup needs quite some time. On my system about 10 seconds. You knwo that everything runs well when the first Note is posted. - The startup needs quite some time. On my system about 10 seconds. You knwo that everything runs well when the first Note is posted.
- When the bot is started, it could happen that he runs in a timeout in the first 600 seconds. To prevent that, just mention the bot and he will stay in a loop. - When the bot is started, it could happen that he runs in a timeout in the first 600 seconds. To prevent that, just mention the bot and he will stay in a loop.
## Works on my machine!

12
docker-compose.yml Normal file
View file

@ -0,0 +1,12 @@
version: "3"
services:
misskey-ebooks-bot:
build:
context: ./
container_name: misskey-ebooks-bot
restart: always
volumes:
- ./bot.cfg:/usr/src/app/bot.cfg
- ./roboduck.db:/usr/src/app/roboduck.db
- ./markov.json:/usr/src/app/markov.json

View file

@ -6,6 +6,7 @@ import mi
import sys import sys
import configparser import configparser
import threading import threading
from pathlib import Path
from mi import Note from mi import Note
from mi.ext import commands, tasks from mi.ext import commands, tasks
from mi.note import Note from mi.note import Note
@ -16,22 +17,22 @@ from roboduck import *
#Load Misskey configuration #Load Misskey configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(os.path.join(os.path.dirname(__file__), 'bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
uri="wss://"+config.get("misskey","instance_write")+"/streaming" uri="wss://"+config.get("misskey","instance_write")+"/streaming"
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__()
@tasks.loop(3600) @tasks.loop(3600)
async def loop_1h(self): async def loop_1h(self):
text = create_sentence() text = create_sentence()
await bot.client.note.send(content=text) await bot.client.note.send(content=text)
@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)
@ -44,8 +45,8 @@ class MyBot(commands.Bot):
self.loop_12h.start() #Launching renew posts every 12 hours self.loop_12h.start() #Launching renew posts every 12 hours
self.loop_1h.start() # self.loop_1h.start() #
print(datetime.now().strftime('%Y-%m-%d %H:%M:%S')+" Roboduck Bot started!") 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="" text=""
if (not note.author.is_bot): if (not note.author.is_bot):
@ -54,17 +55,17 @@ class MyBot(commands.Bot):
text = "@" + note.author.name + " " #Building the reply on same instance text = "@" + note.author.name + " " #Building the reply on same instance
else: else:
text = "@" + note.author.name + "@" + note.author.host + " " #Building the reply on foreign instance 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__":
if (not os.path.exists(os.path.join(os.path.dirname(__file__), 'roboduck.db'))): databasepath = (Path(__file__).parent).joinpath('roboduck.db')
if (not (os.path.exists(databasepath) and os.stat(databasepath).st_size != 0)):
init_bot() init_bot()
bot = MyBot() bot = MyBot()
asyncio.run(bot.start(uri, token, timeout=600)) asyncio.run(bot.start(uri, token, timeout=600))

View file

@ -1,2 +1,6 @@
markovify markovify
configparser configparser
ujson
requests
msgpack
regex

View file

@ -2,10 +2,11 @@ import requests
import json import json
import os import os
import sys import sys
import re import regex
import configparser import configparser
import markovify import markovify
import sqlite3 import sqlite3
from pathlib import Path
from datetime import * from datetime import *
from time import sleep from time import sleep
@ -16,25 +17,25 @@ def check_str_to_bool(text) -> bool:
return False return False
else: else:
return True return True
def get_notes(**kwargs): def get_notes(**kwargs):
noteid = "k" noteid = "k"
sinceid = "" sinceid = ""
min_notes = 0 min_notes = 0
notesList = [] notesList = []
returnList = [] returnList = []
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"]
else: else:
print("Wrong arguments given!") print("Wrong arguments given!")
print("Exiting routine!") print("Exiting routine!")
@ -43,54 +44,49 @@ def get_notes(**kwargs):
print("No arguments given!") print("No arguments given!")
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")
if (config.get("misskey","instance_read") == config.get("misskey","instance_write")):
host=None
else:
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:
if ((init and len(notesList) >= min_notes) or (oldnote == noteid)): if ((init and len(notesList) >= min_notes) or (oldnote == noteid)):
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,
@ -100,172 +96,167 @@ def get_notes(**kwargs):
"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():
notesList.append(jsonObj) notesList.append(jsonObj)
if (len(notesList) == 0): if (len(notesList) == 0):
print("No new notes to load!") print("No new notes to load!")
return 0 return 0
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 = re.sub(r"@([a-zA-Z0-9-]*(\.))*[a-zA-Z0-9-]*\.[a-zA-z]*", '', content) #Remove instance name with regular expression content = regex.sub(r"(?>@(?>[\w\-])+)(?>@(?>[\w\-\.])+)?", '', content) #Remove instance name with regular expression
content = content.replace("::",": :") #Break long emoji chains content = content.replace("::",": :") #Break long emoji chains
content = content.replace("@", "@"+chr(8203)) 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(os.path.join(os.path.dirname(__file__), '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"
databasepath = os.path.join(os.path.dirname(__file__), '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)): print("Roboduck database not already created!")
print("Roboduck database already created!")
print("Exit initialization!") print("Exit initialization!")
sys.exit(0) sys.exit(0)
with open(databasepath, 'r', encoding='utf-8') as emojilist: with open(databasepath, 'r', encoding='utf-8') as emojilist:
database = sqlite3.connect(databasepath) database = sqlite3.connect(databasepath)
data = database.cursor() data = database.cursor()
data.execute("SELECT text FROM notes ORDER BY timestamp DESC LIMIT " + max_notes + ";") data.execute("SELECT text FROM notes ORDER BY timestamp DESC LIMIT " + max_notes + ";")
rows = data.fetchall() rows = data.fetchall()
for row in rows: for row in rows:
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((os.path.join(os.path.dirname(__file__), '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 = os.path.join(os.path.dirname(__file__), '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)):
print("No database found!") print("No database found!")
print("Please run Bot first!") print("Please run Bot first!")
sys.exit(0) sys.exit(0)
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(os.path.join(os.path.dirname(__file__), 'bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
#print(os.path.join(os.path.dirname(__file__), '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"
data = database.cursor() data = database.cursor()
data.execute("DELETE FROM notes WHERE id NOT IN (SELECT id FROM notes ORDER BY timestamp DESC LIMIT " + max_notes + ");") data.execute("DELETE FROM notes WHERE id NOT IN (SELECT id FROM notes ORDER BY timestamp DESC LIMIT " + max_notes + ");")
database.commit() database.commit()
database.close() database.close()
def create_sentence(): def create_sentence():
with open((os.path.join(os.path.dirname(__file__), '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(os.path.join(os.path.dirname(__file__), 'bot.cfg')) config.read((Path(__file__).parent).joinpath('bot.cfg'))
#print(os.path.join(os.path.dirname(__file__), '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
else: else:
tries = 250 tries = 250
max_overlap_ratio = 0.7 max_overlap_ratio = 0.7
max_overlap_total = 15 max_overlap_total = 15
max_words = None max_words = None
min_words = None min_words = None
""" """
#Debug section to rpint the used values #Debug section to rpint the used values
print("These values are used:") print("These values are used:")
@ -276,7 +267,7 @@ def create_sentence():
print("max_words: " + str(max_words)) print("max_words: " + str(max_words))
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,
@ -290,76 +281,73 @@ def create_sentence():
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 = os.path.join(os.path.dirname(__file__), '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)):
print("No database found!") print("No database found!")
print("Please run Bot first!") print("Please run Bot first!")
sys.exit(0) sys.exit(0)
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)
print("Connected to roboduck.db succesfull...") print("Connected to roboduck.db succesfull...")
data = database.cursor() data = database.cursor()
data.execute("SELECT id FROM notes WHERE timestamp = (SELECT MAX(timestamp) FROM notes);") data.execute("SELECT id FROM notes WHERE timestamp = (SELECT MAX(timestamp) FROM notes);")
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()
return return
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!")
database.close() database.close()
print("Cleaning database...") print("Cleaning database...")
clean_database() clean_database()
print("Database cleaned!") print("Database cleaned!")
print("Short sleep to prevent file collison...") print("Short sleep to prevent file collison...")
sleep(10) sleep(10)
print("Calculating new Markov Chain...") print("Calculating new Markov Chain...")
calculate_markov_chain() calculate_markov_chain()
print("Markov Chain saved!") print("Markov Chain saved!")
print("\nUpdate done!") print("\nUpdate done!")
def init_bot(): def init_bot():
notesList = [] notesList = []
databasepath = os.path.join(os.path.dirname(__file__), 'roboduck.db') databasepath = (Path(__file__).parent).joinpath('roboduck.db')
if (os.path.exists(databasepath) and os.stat(databasepath).st_size != 0):
if (os.path.exists(databasepath)):
print("Roboduck database already created!") print("Roboduck database already created!")
print("Exit initialization!") print("Exit initialization!")
sys.exit(0) sys.exit(0)
print("Creating database...") print("Creating database...")
with open(databasepath, "w+", encoding="utf-8") as f: with open(databasepath, "w+", encoding="utf-8") as f:
database = sqlite3.connect(databasepath) database = sqlite3.connect(databasepath)
print("Connected to roboduck.db succesfull...") print("Connected to roboduck.db succesfull...")
print("Creating Table...") print("Creating Table...")
database.execute("CREATE TABLE notes (id CHAR(10) PRIMARY KEY, text CHAR(5000), timestamp INT);") database.execute("CREATE TABLE notes (id CHAR(10) PRIMARY KEY, text CHAR(5000), timestamp INT);")
print("Table NOTES created...") print("Table NOTES created...")
#Load configuration #Load configuration
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(os.path.join(os.path.dirname(__file__), '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:
@ -367,22 +355,21 @@ def init_bot():
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()
print("Notes written...") print("Notes written...")
print("Creating Markov Chain") print("Creating Markov Chain")
calculate_markov_chain() calculate_markov_chain()
print("Markov Chain calculated & saved.\n") print("Markov Chain calculated & saved.\n")
print("Finished initialization!\n") print("Finished initialization!\n")
print("The bot will now be started!") print("The bot will now be started!")

View file

@ -1,3 +1,3 @@
from roboduck import * from roboduck import *
update() update()