Velasco 1.6

- Added ability to change Velasco's probability to answer replies to his
messages, per chat
- Rewritten save-to-file functions for slightly more readability, and
rewritten load-from-file functions accordingly
This commit is contained in:
vylion 2018-02-19 17:07:33 +01:00
parent 14a92ef0f3
commit 7de67c0238
3 changed files with 81 additions and 18 deletions

View file

@ -3,7 +3,7 @@
from markov import * from markov import *
class Chatlog(object): class Chatlog(object):
def __init__(self, ident, chattype, title, text=None, freq=None): def __init__(self, ident, chattype, title, text=None, freq=None, answer=0.5):
self.id = str(ident) self.id = str(ident)
self.type = chattype self.type = chattype
self.title = title self.title = title
@ -18,6 +18,7 @@ class Chatlog(object):
self.count = len(text) self.count = len(text)
else: else:
self.count = 0 self.count = 0
self.answer = answer
self.gen = Markov(text) self.gen = Markov(text)
def set_title(self, title): def set_title(self, title):
@ -31,6 +32,15 @@ class Chatlog(object):
self.freq = freq self.freq = freq
return self.freq return self.freq
def set_answer_freq(self, freq):
if freq > 1:
self.answer = 1
elif freq < 0:
self.answer = 0
else:
self.answer = freq
return self.answer
def add_msg(self, message): def add_msg(self, message):
self.gen.add_text(message + " !kvl") self.gen.add_text(message + " !kvl")
self.count += 1 self.count += 1
@ -41,19 +51,36 @@ class Chatlog(object):
def get_count(self): def get_count(self):
return self.count return self.count
def answering(self, rand):
if self.answer == 1:
return True
elif self.answer == 0:
return False
return rand <= self.answer
def to_txt(self): def to_txt(self):
lines = [self.id] lines = ["DICT=v2"]
lines.append(self.type) lines.append("CHAT_ID=" + self.id)
lines.append(self.title) lines.append("CHAT_TYPE=" + self.type)
lines.append(str(self.freq)) lines.append("CHAT_NAME=" + self.title)
lines.append("dict:") lines.append("MESSAGE_FREQ=" + str(self.freq))
lines.append(str(self.count)) lines.append("ANSWER_FREQ=" + str(self.answer))
lines.append("WORD_COUNT=" + str(self.count))
lines.append("WORD_DICT=")
txt = '\n'.join(lines) txt = '\n'.join(lines)
return txt + '\n' + self.gen.to_json() return txt + '\n' + self.gen.to_json()
def from_txt(text): def from_txt(text):
lines = text.splitlines() lines = text.splitlines()
if(lines[4] == "dict:"): if(parse_line(lines[0]) == "v2"):
new_log = Chatlog(parse_line(lines[1]), parse_line(lines[2]), parse_line(lines[3]), None, int(parse_line(lines[4])), float(parse_line(lines[5])))
new_log.count = int(parse_line(lines[6]))
cache = '\n'.join(lines[8:])
new_log.gen = Markov.from_json(cache)
if new_log.count < 0:
new_log.count = new_log.gen.new_count()
return new_log
elif(lines[4] == "dict:"):
new_log = Chatlog(lines[0], lines[1], lines[2], None, int(lines[3])) new_log = Chatlog(lines[0], lines[1], lines[2], None, int(lines[3]))
new_log.count = int(lines[5]) new_log.count = int(lines[5])
cache = '\n'.join(lines[6:]) cache = '\n'.join(lines[6:])
@ -64,6 +91,12 @@ class Chatlog(object):
else: else:
return Chatlog(lines[0], lines[1], lines[2], lines[4:], int(lines[3])) return Chatlog(lines[0], lines[1], lines[2], lines[4:], int(lines[3]))
def parse_line(line):
s = line.split('=')
if len(s) < 2:
return ""
return s[1]
def fuse_with(chatlog): def fuse_with(chatlog):
self.count += chatlog.count self.count += chatlog.count
self.gen.fuse_with(chatlog.gen) self.gen.fuse_with(chatlog.gen)

View file

@ -58,7 +58,8 @@ class Markov(object):
return Markov(string, True) return Markov(string, True)
def add_text(self, text): def add_text(self, text):
words = trim_and_split(HEAD + " " + text) words = [HEAD]
words.extend(trim_and_split(text))
self.database(words) self.database(words)
def database(self, wordlist): def database(self, wordlist):
@ -81,7 +82,7 @@ class Markov(object):
for i in range(size): for i in range(size):
gen_words.append(w1) gen_words.append(w1)
if w2 == TAIL or not getkey(w1, w2) in self.cache: if w2 == TAIL or not getkey(w1, w2) in self.cache:
print("Generated text") # print("Generated text")
break break
else: else:
w1, w2 = w2, random.choice(self.cache[getkey(w1, w2)]) w1, w2 = w2, random.choice(self.cache[getkey(w1, w2)])

View file

@ -9,7 +9,7 @@ import argparse
import random import random
# Enable logging # Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s - velascobot',
level=logging.INFO) level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -55,7 +55,10 @@ def savechat(chatlog):
def loadchat(path): def loadchat(path):
open_file = open(path, 'r') open_file = open(path, 'r')
try:
chat = Chatlog.from_txt(open_file.read()) chat = Chatlog.from_txt(open_file.read())
except:
pass
open_file.close() open_file.close()
return chat return chat
@ -67,8 +70,9 @@ def help(bot, update):
/explain - I explain how I work. /explain - I explain how I work.
/help - I send this message. /help - I send this message.
/count - I tell you how many messages from this chat I remember. /count - I tell you how many messages from this chat I remember.
/freq - Change the frequency of both my messages and the times I save my learned vocabulary. (Maximum of 100000) /freq - Change the frequency of my messages. (Maximum of 100000)
/speak - Forces me to speak. /speak - Forces me to speak.
/answer - Change the probability to answer to a reply. (Decimal between 0 and 1)
""") """)
def about(bot, update): def about(bot, update):
@ -78,7 +82,7 @@ def explain(bot, update):
update.message.reply_text('I decompose every message I read in groups of 3 consecutive words, so for each consecutive pair I save the word that can follow them. I then use this to make my own messages. At first I will only repeat your messages because for each 2 words I will have very few possible following words.\n\nI also separate my vocabulary by chats, so anything I learn in one chat I will only say in that chat. For privacy, you know. Also, I save my vocabulary in the form of a json dictionary, so no logs are kept.\n\nMy default frequency in private chats is one message of mine from each 2 messages received, and in group chats it\'s 10 messages I read for each message I send.') update.message.reply_text('I decompose every message I read in groups of 3 consecutive words, so for each consecutive pair I save the word that can follow them. I then use this to make my own messages. At first I will only repeat your messages because for each 2 words I will have very few possible following words.\n\nI also separate my vocabulary by chats, so anything I learn in one chat I will only say in that chat. For privacy, you know. Also, I save my vocabulary in the form of a json dictionary, so no logs are kept.\n\nMy default frequency in private chats is one message of mine from each 2 messages received, and in group chats it\'s 10 messages I read for each message I send.')
def echo(bot, update): def echo(bot, update):
text = update.message.text.split(None, 2) text = update.message.text.split(None, maxsplit=1)
if len(text) > 1: if len(text) > 1:
text = text[1] text = text[1]
chatlog.add_msg(text) chatlog.add_msg(text)
@ -109,7 +113,8 @@ def read(bot, update):
chatlog = chatlogs[ident] chatlog = chatlogs[ident]
chatlog.add_msg(update.message.text) chatlog.add_msg(update.message.text)
replied = update.message.reply_to_message replied = update.message.reply_to_message
if (replied is not None) and (replied.from_user.name == "@velascobot") and (random.random() <= 0.5): if (replied is not None) and (replied.from_user.name == "@velascobot") and chatlog.answering(random.random()):
print("They're talking to me, I'm answering back")
msg = chatlog.speak() msg = chatlog.speak()
update.message.reply_text(msg) update.message.reply_text(msg)
if random.random() <= REPT_CHANCE: if random.random() <= REPT_CHANCE:
@ -119,10 +124,13 @@ def read(bot, update):
msg = chatlog.speak() msg = chatlog.speak()
try: try:
if random.random() <= REPL_CHANCE: if random.random() <= REPL_CHANCE:
print("I made a reply")
update.message.reply_text(msg) update.message.reply_text(msg)
else: else:
print("I sent a message")
bot.sendMessage(chatlog.id, msg) bot.sendMessage(chatlog.id, msg)
if random.random() <= REPT_CHANCE: if random.random() <= REPT_CHANCE:
print("And a followup")
msg = chatlog.speak() msg = chatlog.speak()
bot.sendMessage(chatlog.id, msg) bot.sendMessage(chatlog.id, msg)
except TimedOut: except TimedOut:
@ -194,12 +202,32 @@ def set_freq(bot, update):
reply = "Format was confusing; frequency not changed from " + str(chatlogs[ident].freq) reply = "Format was confusing; frequency not changed from " + str(chatlogs[ident].freq)
update.message.reply_text(reply) update.message.reply_text(reply)
def set_answer_freq(bot, update):
ident = str(update.message.chat.id)
if not ident in chatlogs:
chat = update.message.chat
title = get_chatname(chat)
chatlog = Chatlog(chat.id, chat.type, title)
chatlogs[chatlog.id] = chatlog
if not len(update.message.text.split()) > 1:
reply = "Current answer probability is " + str(chatlogs[ident].answer)
else:
try:
value = update.message.text.split()[1]
value = float(value)
value = chatlogs[ident].set_answer_freq(value)
reply = "Probability of answering set to " + str(value)
savechat(chatlogs[ident])
except:
reply = "Format was confusing; answer probability not changed from " + str(chatlogs[ident].answer)
update.message.reply_text(reply)
def stop(bot, update): def stop(bot, update):
global ADMIN_ID global ADMIN_ID
chatlog = chatlogs[update.message.chat.id] chatlog = chatlogs[update.message.chat.id]
del chatlogs[chatlog.id] #del chatlogs[chatlog.id]
os.remove(LOG_DIR + chatlog.id + LOG_EXT) #os.remove(LOG_DIR + chatlog.id + LOG_EXT)
print("I got blocked. Removed user " + chatlog.id) print("I got blocked by user " + chatlog.id)
def main(): def main():
parser = argparse.ArgumentParser(description='A Telegram markov bot.') parser = argparse.ArgumentParser(description='A Telegram markov bot.')
@ -226,6 +254,7 @@ def main():
dp.add_handler(CommandHandler("id", get_id)) dp.add_handler(CommandHandler("id", get_id))
dp.add_handler(CommandHandler("stop", stop)) dp.add_handler(CommandHandler("stop", stop))
dp.add_handler(CommandHandler("speak", speak)) dp.add_handler(CommandHandler("speak", speak))
dp.add_handler(CommandHandler("answer", set_answer_freq))
# on noncommand i.e message - echo the message on Telegram # on noncommand i.e message - echo the message on Telegram
# dp.add_handler(MessageHandler(Filters.text, echo)) # dp.add_handler(MessageHandler(Filters.text, echo))