mirror of
https://github.com/fotoente/MIsskey-ebooks-bot
synced 2024-11-29 06:40:44 +00:00
commit
c77a99fe5f
9 changed files with 201 additions and 138 deletions
9
.dockerignore
Normal file
9
.dockerignore
Normal 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
26
.github/workflows/dockerhub.yml
vendored
Normal 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
11
Dockerfile
Normal 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" ]
|
39
README.md
39
README.md
|
@ -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
12
docker-compose.yml
Normal 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
|
27
rdbot.py
27
rdbot.py
|
@ -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))
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
markovify
|
markovify
|
||||||
configparser
|
configparser
|
||||||
|
ujson
|
||||||
|
requests
|
||||||
|
msgpack
|
||||||
|
regex
|
||||||
|
|
207
roboduck.py
207
roboduck.py
|
@ -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!")
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
from roboduck import *
|
from roboduck import *
|
||||||
|
|
||||||
update()
|
update()
|
||||||
|
|
Loading…
Reference in a new issue