* 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>
Linkbot listens for http:// and https:// links, and attempts to get
their titles.
This also adds the "tesla" dependency to mix.exs
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Previously, if a message was supposed to be handled by 3 modules, they
would all be handled, synchronously, in the same process. If any of them
crashed, it would affect any other modules that needed to be processed
ahead of it.
Now, a new task is spun up for each module, so module handlers are now
indpendent.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
* Present rooms are tracked by Omnibot.Core now, instead of
Omnibot.State
* Channel synchronization is done through Omnibot.Core instead of
Omnibot.Irc
* Add Omnibot.Module.on_init/1 callback, which returns a "state" value
for the module to keep track of. By default, the state is nil.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Omnibot.Router was useful when it was used for actually routing
messages. However, its responsibilities have spread such that it's just
a single function at this point. So this single function has been moved
to Omnibot.Irc as a private function instead.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Previously, core behavior was handled in the Omnibot.Router module.
However, since the module system is robust enough, this work has been
delegated to the Omnibot.Core module.
Additionally, the Omnibot.State module has been augmented to keep track
of the modules that have been currently loaded, and methods that are
used to get the list of loaded modules and routing information for those
modules are coming from Omnibot.State as well (as opposed to
Omnibot.Config). This is to avoid requiring the Omnibot.Core module be
included in the config, plus avoiding modification of the runtime config
after it has already been loaded.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
The problem:
When we're going through the list of modules to send messages to based
on the channels they're a part of, it was being done so through the
config. Since the config doesn't (and shouldn't) list all of the core
modules that get included, any core modules that were loaded and running
under the ModuleSupervisor would not get included in the router's
attempt to send messages to a module.
Now, the Config.all_channels and Config.channel_modules functions live
in State, and State has a new "add_loaded_module" function where loaded
modules are registered. The aforementioned moved functions will use this
as the "source of truth" when deciding where to send messages for
modules to handle.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Since modules can now intercept all messages in the channels they're
listening for, it'd be cool to have modules handling things like making
sure the Omnibot.State stays updated as appropriate, and that pings are
ponged, etc.
This will probably deprecate the router, since it's been reduced to a
single function call, but we'll see about that.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Modules are now defined using on_{msg,channel_msg,join,part,kick}.
Additionally, commands can be defined using a convenient macro.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
Modules can easily be defined with `use Omnibot.Module`.
Omnibot.Contrib.Fortune has been updated to use this.
Signed-off-by: Alek Ratzloff <alekratz@gmail.com>