Age | Commit message (Collapse) | Author |
|
A test to try to get _something_ to compile. And it does!
Here we try to run a plugin with `runReaderT` and `runBot` to pass
`Options` to the plugin, using the "help" plugin as an example. It feeds
hard-coded `Options` to the plugin and outputs its response to STDOUT.
Also get an implementation of `initializePlugins` working that will
allow us to build a list of plugins with `Options` applied to the
reader.
This commit comments out the whole of IRC.hs in order to get the code to
compile. That file still has a bunch of `Bot` monad-related type errors.
To build the final plugin list, I think what we'll want to do is use
something like `initializePlugins` in Lib to apply all our plugins with
the `Options` and then feed that to `connectIRC`. In order to build the
"help" plugin, the plan is to re-apply the `Options` after `ask`ing for
them inside the `PluginAction` for "help" to all plugins, thus enabling
us to get a plugin list with the applied reader for proper localisation
of the help output.
|
|
Convert this function and `Plugins(performPlugin)` to use a
`Bot`-wrapped plugin.
Still having a lot of trouble with this, unable to get past
monad-related compilation errors. The plan is to leave this as-is for
now and attack the problem from a different place, looking at the `main`
function next, applying our plugins with `runBot`, and then passing
those monad-wrapped plugins to `connectIRC`. Once that's taken care of,
we'll see what we need to modify in this middle strata of the stack.
|
|
OMG it works!!! This took me a week of off-and-on tinkering, learning,
procrastinating, and struggling. So awesome that it's finally working!
The `liftMaybe` was key, learned that from:
https://stackoverflow.com/questions/8684252/how-to-inject-a-maybe-value-into-maybet
This article on monad transformers by 'kqr' also helped quite a bit:
https://github.com/kqr/gists/blob/master/articles/gentle-introduction-monad-transformers.md
A Wikibooks article on the same was somewhat useful:
https://en.wikibooks.org/wiki/Haskell/Monad_transformers
And this was an interesting look at Alternatives, which I didn't end up
using but was interesting nonetheless:
http://www.parsonsmatt.org/2016/11/18/clean_alternatives_with_maybet.html
When I learned about monad transformers I thought: "Yes! This is what I
need to clean up the `privmsgFromPlugin` function.". I wasn't liking the
nested `case` statements there, and since everything was a `Maybe`, I
thought, why can't we chain the `Maybe`s like we're supposed to be able
to with monads? Well, turns out we can. It just involved a lot of tricky
`lift`ing. This gets rid of the nesting, resulting in a much cleaner-
looking function. So cool!
|
|
When talking with the bot in a private one-to-one query, there's no
reason to use the "sorbot: " prefix because you're not talking to anyone
else. In that case, just behave like we did before and allow users to
send commands with no prefix.
|
|
Prevent Sorbot from responding to general messages. Messages must be
directed at Sorbot to invoke plugins and get a response.
|
|
If the plugin is defined as `queryOnly`, the response shouldn't be sent
on the channel to everyone, it should instead be sent directly to the
user in a private query message.
Enable this functionality for the Help plugin so that help output only
gets sent to the user requesting help. This ensures other channel
participants don't get an annoyingly long section of output that they
didn't ask for.
|
|
Instead of waiting 1 second between posting messages, pause 0.2 seconds
for faster posting. This may cause flood concerns, but improves
responsiveness from commands that send multi-line responses, like Help
and Factorial.
|
|
The IRC protocol doesn't permit messages longer than 512 bytes
(https://news.ycombinator.com/item?id=7991049). This includes server,
channel, hostname, etc. information that's also included in the message.
In order to be able to send longer messages, chop them at 400 characters
(https://wholok.com/irc/), which seems like a "reasonable" hard-coded
value.
We already split our messages at newlines, so we need to effectuate a
double split here. First we split at 400 characters because we might
have a line that runs longer than that before encountering a newline. To
do this, we use the `chunksOf` function
(https://wiki.haskell.org/Data.List.Split).
Then, when splitting on newlines, we take the list produced by splitting
at 400 characters, and must split the elements inside that list. This
creates a two-dimensional array. In order to flatten the array, we use
the `concat` function
(https://stackoverflow.com/questions/5994051/is-there-a-function-to-flatten-a-nested-list-of-elements).
|
|
Instead of printing all help text on a single line as a result of
joining the list of help text, print each plugin's help on a separate
line.
Also separate commands from descriptions with a dash instead of a tab as
the tab character was getting rendered as an `I` in irssi instead of the
actual whitespace I had been hoping for. The dash in inspired by Hubot.
In order to print multiple lines of output, we needed to change the IRC
PRIVMSG handler. This now splits the plugin result at newlines into a
list and sends separate PRIVMSGs for each line of output. Before, text
with newlines would only show the first line in the resulting IRC
message.
Assume plugin error messages will always be a single line.
|
|
Previously I had moved the `{` to the previous line because I was
getting compilation errors with it on a new line. Turns out whitespace
is important here. We just need to indent it an extra time and the
compiler understands us. Now this section matches the style of the rest
of the project.
|
|
Use the `Data.Text` type instead of `String` in most of the places we
use it in `Plugin` and `Message`.
This allows us to more easily pass data between the IRC package. No more
kludgy `pack`s and `unpack`s in our IRC message handler.
The one thing we couldn't convert was our regex. From what I understand
(https://stackoverflow.com/questions/14922579/haskell-regular-expressions-and-data-text#14922626),
the regex library I'm using doesn't support `Data.Text`, so use
`String`s for that instead.
|
|
When a PRIVMSG comes in from an individual user instead of a channel,
invoke our plugins so we're doing the same thing in both cases.
|
|
This was there as a test but didn't work. It's no longer needed, so we
can remove it.
|
|
Handle our un-handled `Nothing` branches. Make `privmsgFromPlugin`
return a `Maybe` so we can decide whether or not to send a message
upstream.
If we do get a `Nothing` from `privmsgFromPlugin`, just return unit and
don't send any chat message, since no plugin matched and thus none could
respond.
|
|
We have some code that's duplicated in both branches of the `Either`
case statement that sends a response over IRC. Extract that to a new
function that can be shared between the two case statements to try to
reduce repetition.
Took me a while to wrangle the monads to get this, but it's in working
condition now. Need to add the commented `Nothing` branch in so I'm
handling all the cases.
|
|
I had a bunch of code commented from earlier and from false starts when
working on integrating my plugins and the IRC message handler. Remove
these commented lines now as they are no longer relevant. The actual
code is functioning.
|
|
Instead of sending a hard-coded string message over IRC, now invoke our
plugin list.
Now, IRC messages get matched against our plugin list, and if any plugin
matches, its return string gets posted to the appropriate channel.
Move the database connection inside the plugin so that we can continue
to access it without having to pass in the DB connection. I didn't want
to connect to the database in the IRC code because it doesn't relate,
and I don't have a good enough grasp of monads to know if I can create
the connection in "Lib.hs" and "pass" it to the IRC message handler to
then pass it into the plugins.
Moving the database connection inside the plugins means that it no
longer takes a database connection as an argument.
The IRC message handler now has a lot of duplication, but it basically
works, which is super exciting! Took a _long_ time to figure out the
proper way to get the types to line up and get this compiling, but
really glad it's working now! The magic function turned out to be
`liftIO` to extract the `Either String String` out of its `IO` monad
wrapper to be fed to the `IRC.send` function.
Not a fan of the type conversion between `String` and `Data.Text`, so
we'll eventually have to convert our plugins to use `Data.Text`.
|
|
Now that we have something sort of working on the IRC side of things, we
can move it out into its own module to keep `Lib` clear and tidy.
|