Touched up the README.md some more

Added actual minimum period checks accross all the code
This commit is contained in:
vylion 2020-10-29 10:38:19 +01:00
parent 175c006229
commit ec14abcaff
5 changed files with 30 additions and 22 deletions

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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)