mirror of
https://gitlab.com/vylion/velascobot.git
synced 2025-04-19 13:36:36 +02:00
Touched up the README.md some more
Added actual minimum period checks accross all the code
This commit is contained in:
parent
175c006229
commit
ec14abcaff
5 changed files with 30 additions and 22 deletions
10
README.md
10
README.md
|
@ -1,14 +1,14 @@
|
||||||
# Velascobot
|
# 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.
|
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
|
## 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
|
## Commands & ussage
|
||||||
|
|
||||||
|
@ -38,9 +38,9 @@ Sending the command on its own (e.g. `/period`) tells you the current value. Sen
|
||||||
|
|
||||||
### Answer
|
### 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
|
### Restricted
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ class Archivist(object):
|
||||||
vocab = Generator.loads(vocab_dump)
|
vocab = Generator.loads(vocab_dump)
|
||||||
else:
|
else:
|
||||||
vocab = Generator()
|
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:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -131,6 +131,9 @@ class Archivist(object):
|
||||||
if reader.period() > self.max_period:
|
if reader.period() > self.max_period:
|
||||||
reader.set_period(self.max_period)
|
reader.set_period(self.max_period)
|
||||||
self.store(*reader.archive())
|
self.store(*reader.archive())
|
||||||
|
elif reader.period() < self.min_period:
|
||||||
|
reader.set_period(self.min_period)
|
||||||
|
self.store(*reader.archive())
|
||||||
yield reader
|
yield reader
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error("Failed passing through {}".format(dirname))
|
self.logger.error("Failed passing through {}".format(dirname))
|
||||||
|
|
24
reader.py
24
reader.py
|
@ -34,7 +34,7 @@ class Reader(object):
|
||||||
ANIM_TAG = "^IS_ANIMATION^"
|
ANIM_TAG = "^IS_ANIMATION^"
|
||||||
VIDEO_TAG = "^IS_VIDEO^"
|
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
|
# The Metadata object holding a chat's specific bot parameters
|
||||||
self.meta = metadata
|
self.meta = metadata
|
||||||
# The Generator object holding the vocabulary learned so far
|
# The Generator object holding the vocabulary learned so far
|
||||||
|
@ -51,22 +51,22 @@ class Reader(object):
|
||||||
self.names = names
|
self.names = names
|
||||||
|
|
||||||
# Create a new Reader from a Chat object
|
# 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))
|
meta = Metadata(chat.id, chat.type, get_chat_title(chat))
|
||||||
vocab = Generator()
|
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
|
# 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
|
return None
|
||||||
|
|
||||||
# Create a new Reader from a meta's file dump
|
# Create a new Reader from a meta's file dump
|
||||||
def FromCard(meta, vocab, max_period, logger):
|
def FromCard(card, vocab, min_period, max_period, logger):
|
||||||
metadata = Metadata.loads(meta)
|
meta = Metadata.loads(card)
|
||||||
return Reader(metadata, vocab, max_period, logger)
|
return Reader(meta, vocab, min_period, max_period, logger)
|
||||||
|
|
||||||
# Deprecated: this method will be removed in a new version
|
# 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(...))",
|
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.")
|
"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]
|
version = version if len(version.strip()) > 1 else lines[4]
|
||||||
logger.info("Dictionary version: {} ({} lines)".format(version, len(lines)))
|
logger.info("Dictionary version: {} ({} lines)".format(version, len(lines)))
|
||||||
if version == "v4" or version == "v5":
|
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
|
# I stopped saving the chat metadata and the cache together
|
||||||
elif version == "v3":
|
elif version == "v3":
|
||||||
meta = Metadata.loadl(lines[0:8])
|
meta = Metadata.loadl(lines[0:8])
|
||||||
|
@ -95,7 +95,7 @@ class Reader(object):
|
||||||
cache = lines[4:]
|
cache = lines[4:]
|
||||||
vocab = Generator(load=cache, mode=Generator.MODE_LIST)
|
vocab = Generator(load=cache, mode=Generator.MODE_LIST)
|
||||||
# raise SyntaxError("Reader: Metadata format unrecognized.")
|
# raise SyntaxError("Reader: Metadata format unrecognized.")
|
||||||
r = Reader(meta, vocab, max_period, logger)
|
r = Reader(meta, vocab, min_period, max_period, logger)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
# Returns a nice lice little tuple package for the archivist to save to file.
|
# 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
|
# Sets a new period in the Metadata
|
||||||
def set_period(self, period):
|
def set_period(self, period):
|
||||||
# The period has to be under max_period; otherwise, truncate to max_period
|
# The period has to be in the range [min..max_period]; otherwise, clamp to said range
|
||||||
new_period = min(period, self.max_period)
|
new_period = max(self.min_period, min(period, self.max_period))
|
||||||
set_period = self.meta.set_period(new_period)
|
set_period = self.meta.set_period(new_period)
|
||||||
if new_period == set_period and new_period < self.countdown:
|
if new_period == set_period and new_period < self.countdown:
|
||||||
# If succesfully changed and the new period is less than the current
|
# If succesfully changed and the new period is less than the current
|
||||||
|
|
11
speaker.py
11
speaker.py
|
@ -62,7 +62,8 @@ class Speaker(object):
|
||||||
self.mute_timer = None
|
self.mute_timer = None
|
||||||
# The bot's username, "@" included
|
# The bot's username, "@" included
|
||||||
self.username = username
|
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
|
self.max_period = archivist.max_period
|
||||||
|
|
||||||
# The Archivist functions to load and save from and to files
|
# The Archivist functions to load and save from and to files
|
||||||
|
@ -136,7 +137,7 @@ class Speaker(object):
|
||||||
|
|
||||||
reader = self.get_reader_file(cid)
|
reader = self.get_reader_file(cid)
|
||||||
if not reader:
|
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)
|
old_reader = self.memory.add(reader)
|
||||||
if old_reader is not None:
|
if old_reader is not None:
|
||||||
|
@ -289,8 +290,10 @@ class Speaker(object):
|
||||||
send(bot, cid, self.speech(reader), replying, logger=self.logger, **kwargs)
|
send(bot, cid, self.speech(reader), replying, logger=self.logger, **kwargs)
|
||||||
if self.bypass:
|
if self.bypass:
|
||||||
# Testing mode, force a reasonable period (to not have the bot spam one specific chat with a low period)
|
# Testing mode, force a reasonable period (to not have the bot spam one specific chat with a low period)
|
||||||
max_period = self.max_period
|
minp = self.min_period
|
||||||
reader.set_period(random.randint(max_period // 4, max_period))
|
maxp = self.max_period
|
||||||
|
rangep = maxp - minp
|
||||||
|
reader.set_period(random.randint(rangep // 4, rangep) + minp)
|
||||||
if random.random() <= self.repeat:
|
if random.random() <= self.repeat:
|
||||||
send(bot, cid, self.speech(reader), logger=self.logger, **kwargs)
|
send(bot, cid, self.speech(reader), logger=self.logger, **kwargs)
|
||||||
# Consider any Network Error as a Telegram temporary ban, as I couldn't find
|
# Consider any Network Error as a Telegram temporary ban, as I couldn't find
|
||||||
|
|
|
@ -100,6 +100,8 @@ def main():
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
assert args.max_period >= args.min_period
|
||||||
|
|
||||||
# Create the EventHandler and pass it your bot's token.
|
# Create the EventHandler and pass it your bot's token.
|
||||||
updater = Updater(args.token, use_context=True)
|
updater = Updater(args.token, use_context=True)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue