* Markov chains will train and generate chains correctly now
* Implement Markov.save_chains/0
* Add a couple more utils that help accomplish the above
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
* Wordbot does not IO.inspect() the winners of the round
* Split Omnibot.Plugin.base_children/1 into base_children_before/1 and
base_children_after/1 for plugins that come before and after the
children in the module
* Other minor changes
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
* Plugins all derive from Omnibot.Plugin. There still is a base plugin,
in case we want to have another plugin backend instead of a GenServer
* All plugins are monitored by a unique Plugin.Supervisor, which is in
turn started by the PluginSupervisor (yes this is confusing, yes it
needs to be renamed)
* Any other auxiliary child processes may be started through the
Plugin.children/1 function.
* By default, plugins have a CfgState process which is an Agent that
keeps track of the plugin's configuration and state
* Plugin API is now called through the GenServer backend for better
synchronicity.
* Very few changes to the front-facing Plugin API, which is nice
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
* using Plugin.GenServer now uses Plugin.Base so that is not required
* Remove Omnibot.Plugin because all it did was include Plugin.GenServer
and Plugin.Base
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Both markov and wordbot have some auxiliary processes that run to keep
track of things. Previously, they both had custom supervisors grafted on
to the Plugin.Base - now, this grafting is automated with
Plugin.Supervisor.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
* Markov chains record words correctly from a single line to the end of
their line with a couple of exceptions.
* Start working on using ETS for storing markov chains, and saving it as
a DETS periodically
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
* train/2 takes a chain and a list of words to train them on
* add_weight/4 takes a chain, a list of words (the key), the word it
points to, and an optional increment which will increment the value of
a weight, or insert the weight.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
For some reason there doesn't appear to be a binary search function in
Elixir's standard library, so this implements that.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Being able to handle arbitrary messages from other processes is a
generally useful thing to have for fine-grained control of the plugin.
Agents don't provide that, while GenServers do. I've removed all
instances of the Agent and replaced it with the GenServer and everything
appears fine so far.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Since route_msg expects msg.prefix to be present, every plugin that
calls route_msg when no prefix is specified in the message (e.g. with
PING messages) would cause a crash of the process.
Now, it just calls a no-op function, since all behavior covered by
route_message requires information in the prefix at some point.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Linkbot titles now check the "meta" tags for either "og:title" or
"title" attributes. This is usually a more accurate/correct title than
using the "title" tag, but this is checked as a last resort.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Nicknames that are sent by wordbot for the leaderboard command and the
end-of-game scoreboard are split up by zero-width space characters, to
avoid pinging the users at random times.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Wordbot implementation now uses the new Omnibot.Module.GenServer module,
which uses a GenServer instead of an Agent. This way, the module can
receive messages and makes storage a little easier.
Beyond that, minor changes all around.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Wordbot is a little more complex of a bot module and I've been working
on it here.
Other than wordbot module, a few minor tweaks have been added all around
that don't really affect anything.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Add Omnibot.Module.Agent which implements the basic agent cfg + state
persistence functionality, which is then included in the Omnibot.Module.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Previously, tasks would be started with an auxiliary task that would
time out based on the module's timeout value - i.e. two tasks per module
per message. This was a little silly, so I've migrated to using
Task.Supervisor.async_stream_nolink/4. The only downside is that
module-defined timeout is not available for config, because all function
calls need to have the same timeout. This can probably be fixed by
breaking down the async_stream_nolink() function, but for now setting a
hard 30 second timeout works well enough.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Previously, all modules were Agents. This renames the Omnibot.Module to
be the base module, and the new Omnibot.Module uses an Agent by default.
This opens the doors to possibly allowing metamodule supervisors for
modules that may be more complex.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Linkbot will now block addresses that attempt to use localhost, *.local,
*.home, *.localdomain, hosts that don't have a dot in them, and IP
addresses. This is to avoid exposing the bot to local addresses on the
host computer.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
- Using the @default_config attribute in a module will fill out a default
value.
- cfg[:timeout] sets the timeout for the task for this module to finish.
- Update Omnibot.Irc.route_msg/2 to have each task spawn a second task,
while the first task waits for its child to finish, otherwise killing
it.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>