From ec14abcaff95d2c87644ff5906cd53258f3a564d Mon Sep 17 00:00:00 2001 From: vylion Date: Thu, 29 Oct 2020 10:38:19 +0100 Subject: [PATCH] Touched up the README.md some more Added actual minimum period checks accross all the code --- README.md | 10 +++++----- archivist.py | 5 ++++- reader.py | 24 ++++++++++++------------ speaker.py | 11 +++++++---- velasco.py | 2 ++ 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f68fbc0..01c05ee 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Velascobot -This is yet another Markov chain-based chatbot, based on the Twitterbot fad consisting of creating a bot account that would try to generate new random tweets (usually having `_ebooks` or `.txt` to indicate that an account was one of such, or just a plain `bot` suffix), using your own as a template. However, instead of reading the messages from a Twitter account, this bot is made to read the messages in a group chat, and try to blend in by generating new messages that fit the patterns seen in that specific group chat. At the beginning that will mean a lot of parroting, but eventually the bot starts coming up with sentences of itself. +This is yet another Markov chain-based chatbot, based on the Twitterbot fad consisting of creating a bot account that would try to generate new random tweets (usually having `_ebooks` or `.txt` in their names to indicate that an account was one of such, or just a plain `bot` suffix), using your own as a template. However, instead of reading the messages from a Twitter account, this bot is made to read the messages in a group chat, and try to blend in by generating new messages that fit the patterns seen in that specific group chat. At the beginning that will mean a lot of parroting, but eventually the bot starts coming up with sentences of itself. This bot also works on private chats between a user and itself, but of course the training is much lower and it will feel like talking to a parrot for a longer time, unless you feed it a lot of messages quickly. ## How to use it -You have to add the bot to a chat group, letting it rad and send messages. Maybe set some configuration commands too. +You have to add the bot to a chat group, or speak to it privately, letting it read and send messages. Maybe set some configuration commands too. -If you want to clone or fork this repo and host your own instance of Velasco, see MANUAL.md. +If you want to clone or fork this repo and host your own instance of Velasco, see [MANUAL.md](MANUAL.md). ## Commands & ussage @@ -38,9 +38,9 @@ Sending the command on its own (e.g. `/period`) tells you the current value. Sen ### Answer -This value is the chance of the bot to answer to a message that is in turn a reply to one of its own messages, or (to be implemented:) to a message that mentions it. The default value is 0.5 (50% chance). The maximum is 1 (100% chance) and to disable it you must set it to 0 (0% chance). +This value is the chance of the bot to answer to a message that is in turn a reply to one of its own messages, or to a message that mentions the bot (see above: [Summon](###Summon)). The default value is `0.5` (50% chance). The maximum is `1` (100% chance) and to disable it you must set it to 0 (0% chance). -Sending the command on its own (e.g. `/answer`) tells you the current value. Sending a positive decimal number between 0 and 1 inclusive (e.g. `/answer 0.95`) will set it as the new value. +Sending the command on its own (e.g. `/answer`) tells you the current value. Sending a positive decimal number between `0` and `1` inclusive (e.g. `/answer 0.95`) will set it as the new value. ### Restricted diff --git a/archivist.py b/archivist.py index 051a413..9b4ad0d 100644 --- a/archivist.py +++ b/archivist.py @@ -103,7 +103,7 @@ class Archivist(object): vocab = Generator.loads(vocab_dump) else: vocab = Generator() - return Reader.FromCard(card, vocab, self.max_period, self.logger) + return Reader.FromCard(card, vocab, self.min_period, self.max_period, self.logger) else: return None @@ -131,6 +131,9 @@ class Archivist(object): if reader.period() > self.max_period: reader.set_period(self.max_period) self.store(*reader.archive()) + elif reader.period() < self.min_period: + reader.set_period(self.min_period) + self.store(*reader.archive()) yield reader except Exception as e: self.logger.error("Failed passing through {}".format(dirname)) diff --git a/reader.py b/reader.py index 4c41ca6..7f3aa16 100644 --- a/reader.py +++ b/reader.py @@ -34,7 +34,7 @@ class Reader(object): ANIM_TAG = "^IS_ANIMATION^" VIDEO_TAG = "^IS_VIDEO^" - def __init__(self, metadata, vocab, max_period, logger, names=[]): + def __init__(self, metadata, vocab, min_period, max_period, logger, names=[]): # The Metadata object holding a chat's specific bot parameters self.meta = metadata # The Generator object holding the vocabulary learned so far @@ -51,22 +51,22 @@ class Reader(object): self.names = names # Create a new Reader from a Chat object - def FromChat(chat, max_period, logger): + def FromChat(chat, min_period, max_period, logger): meta = Metadata(chat.id, chat.type, get_chat_title(chat)) vocab = Generator() - return Reader(meta, vocab, max_period, logger) + return Reader(meta, vocab, min_period, max_period, logger) # TODO: Create a new Reader from a whole Chat history - def FromHistory(history, vocab, max_period, logger): + def FromHistory(history, vocab, min_period, max_period, logger): return None # Create a new Reader from a meta's file dump - def FromCard(meta, vocab, max_period, logger): - metadata = Metadata.loads(meta) - return Reader(metadata, vocab, max_period, logger) + def FromCard(card, vocab, min_period, max_period, logger): + meta = Metadata.loads(card) + return Reader(meta, vocab, min_period, max_period, logger) # Deprecated: this method will be removed in a new version - def FromFile(text, max_period, logger, vocab=None): + def FromFile(text, min_period, max_period, logger, vocab=None): print("Warning! This method of loading a Reader from file (Reader.FromFile(...))", "is deprecated, and will be removed from the next update. Use FromCard instead.") @@ -76,7 +76,7 @@ class Reader(object): version = version if len(version.strip()) > 1 else lines[4] logger.info("Dictionary version: {} ({} lines)".format(version, len(lines))) if version == "v4" or version == "v5": - return Reader.FromCard(text, vocab, max_period, logger) + return Reader.FromCard(text, vocab, min_period, max_period, logger) # I stopped saving the chat metadata and the cache together elif version == "v3": meta = Metadata.loadl(lines[0:8]) @@ -95,7 +95,7 @@ class Reader(object): cache = lines[4:] vocab = Generator(load=cache, mode=Generator.MODE_LIST) # raise SyntaxError("Reader: Metadata format unrecognized.") - r = Reader(meta, vocab, max_period, logger) + r = Reader(meta, vocab, min_period, max_period, logger) return r # Returns a nice lice little tuple package for the archivist to save to file. @@ -117,8 +117,8 @@ class Reader(object): # Sets a new period in the Metadata def set_period(self, period): - # The period has to be under max_period; otherwise, truncate to max_period - new_period = min(period, self.max_period) + # The period has to be in the range [min..max_period]; otherwise, clamp to said range + new_period = max(self.min_period, min(period, self.max_period)) set_period = self.meta.set_period(new_period) if new_period == set_period and new_period < self.countdown: # If succesfully changed and the new period is less than the current diff --git a/speaker.py b/speaker.py index 434800c..71bd0bb 100644 --- a/speaker.py +++ b/speaker.py @@ -62,7 +62,8 @@ class Speaker(object): self.mute_timer = None # The bot's username, "@" included self.username = username - # The maximum chat period for this bot + # The minimum and maximum chat period for this bot + self.min_period = archivist.min_period self.max_period = archivist.max_period # The Archivist functions to load and save from and to files @@ -136,7 +137,7 @@ class Speaker(object): reader = self.get_reader_file(cid) if not reader: - reader = Reader.FromChat(chat, self.max_period, self.logger) + reader = Reader.FromChat(chat, self.min_period, self.max_period, self.logger) old_reader = self.memory.add(reader) if old_reader is not None: @@ -289,8 +290,10 @@ class Speaker(object): send(bot, cid, self.speech(reader), replying, logger=self.logger, **kwargs) if self.bypass: # Testing mode, force a reasonable period (to not have the bot spam one specific chat with a low period) - max_period = self.max_period - reader.set_period(random.randint(max_period // 4, max_period)) + minp = self.min_period + maxp = self.max_period + rangep = maxp - minp + reader.set_period(random.randint(rangep // 4, rangep) + minp) if random.random() <= self.repeat: send(bot, cid, self.speech(reader), logger=self.logger, **kwargs) # Consider any Network Error as a Telegram temporary ban, as I couldn't find diff --git a/velasco.py b/velasco.py index fc1da66..c1bc703 100644 --- a/velasco.py +++ b/velasco.py @@ -100,6 +100,8 @@ def main(): args = parser.parse_args() + assert args.max_period >= args.min_period + # Create the EventHandler and pass it your bot's token. updater = Updater(args.token, use_context=True)