Age | Commit message (Collapse) | Author |
|
This type will be used as an application-level monad, enabling us to
transmit configuration coming from CLI arguments (`Options`) to any
function that needs it.
The immediate benefit is that we'll be able to easily transmit the
specified language to functions to allow them to use the right
translation string.
|
|
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!
|
|
This plugin calculates factorials. It's not super optimised, but works
for small inputs.
Adds the 'text-show' plugin to convert the resulting integer to a
`Data.Text` to be returned by the plugin action.
At first I had tried /^\d+!$/ for the regex match, but that produced
this error:
sorbot/src/Plugin/Factorial.hs:14:24: error:
lexical error in string/character literal at character 'd'
Tried to fix that by using /^\\d+!$/, but that didn't match anything.
Then tried a long-form character class, /^[:digit:]+!$/, which TDFA
implies it supports, but a quick look at the code seems to indicate that
those parts were removed or commented out. Not sure what was going on
with that. So instead went for the remaining solution, a `0-9` character
class.
|
|
Asking for `Plugin.Plugin...` is redundant. Take this module out of the
`Plugin` directory to eliminate the repetition in naming.
|
|
A new plugin that displays a help listing for every plugin in the bot.
Currently the formatting is off in the chat output, but it does work.
This introduces two new record fields on `Plugin`: `command` and
`description`. The command is the text used to invoke the plugin and the
description is a long form explanation of what the plugin does.
Needed to update the `Show` for `Plugin` to match these extra fields.
Didn't change any of the output for now because I'm not really using the
`show` function, so I don't need to see the new fields for now. Also
change the `p` argument to an `_` because we're not using it.
All existing plugins now have the new fields filled. The Help plugin
will go through the list of all plugins and get their help fields for
output.
In order to be able to use the plugin list in both `Plugin.hs` and in
the Help plugin module, I needed to move the list into a new module to
avoid a circular dependency. Previously the `Plugin` module defined the
list, but we can't import `Plugin` from `Help` because `Plugin` needs to
import `Help` in order to build the full list of plugins. The
semi-hackish solution I came up with was to create a new module for the
plugin list that both these modules can use, but leave out the `Help`
plugin from the plugin list there. Then, `Plugin` and `Help` override
the list, appending the `Help` plugin to the list. I want the Help
plugin to appear last, which is why I'm appending. Wasn't comfortable
concatenating the list because of the performance smell, but it's going
to be a small enough list anyway that it shouldn't be a problem.
One thing I don't really like is the fact that we have to return an
`IO a` from `helpAction` even though it doesn't interact with `IO` at
all. Not sure if there's a way to use `IO` when we need it and not when
we don't. Not a huge deal though.
|
|
Use 'optparse-applicative' to parse command line options.
Provide a `--slack-token` option that sets a Slack API token to enable
access to the chat platform.
More options will come as needed (including things like database name,
language, possibly IRC configuration).
Code is based on the example in
https://hackage.haskell.org/package/optparse-applicative
Pretty cool option parser.
|
|
Get rid of this package because we're no longer using it. Instead we're
going it alone for i18n (dde184a6e24b88f8d6628f4652d5c42ed0f016b2).
Also delete the translation .msg file.
|
|
en.msg:
* Remove comment lines because apparently comments aren't allowed by the
parser
* Change data type to `T.Text` to match the import from the `I18n`
module
GitHubCommit.hs:
Import the `I18n` module because we'll be testing it in context in a
bit, but for now just want to get the module code to compile.
I18n.hs:
Follow the example on http://www.yesodweb.com/book/internationalization
to make a function for proper internationalisation.
|
|
To allow us to take advantage of its `Text.Shakespeare.I18N` feature.
|
|
This plugin provides a command to set a git commit repo URL for use with
the `GitHubCommit` plugin. Typing
git remote set origin URL
in chat will set that URL to the current channel.
Problems:
* Can't figure out how to use capture groups, so the entire matched
message string comes back, not just the URL
* Need to upsert instead of insert into the database
|
|
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.
|
|
Add the 'irc-client' package to facilitate communication over IRC.
Copy the example from:
http://hackage.haskell.org/package/irc-client-0.4.4.4/docs/Network-IRC-Client.html
which connects to a network. We specify a channel to join, and what do
you know, it works! Pretty cool.
Commented out the code that runs the GitHub Commit plugin for now while
testing this.
Add the `OverloadedStrings` extension so we can write string literals
but have them converted into the appropriate `Data.Text` and
`Data.ByteString` types required by the IRC library.
|
|
The GitHub Commit plugin needs access to the database in order to work
properly. Include 'sqlite-simple' to give us access to the database and
have it transform rows into Haskell objects.
This change has the unfortunate effect of forcing us to make
`PluginAction` an IO type. This means we'll need to make all our plugin
action functions use IO, even the pure ones. `PluginAction` now takes a
database connection as a second argument, and returns an `IO String`.
I don't like the fact that the database argument is effectively
hard-coded. Thus the TODO note to try to make a type class to replace it
so we can pass a null database connection when it isn't needed.
Eventually, if more things like this need to be passed into the
function, we might consider making a new struct type for the purpose.
In order to be able to use the `query_` function, which takes a
String-like `Query`, we have to declare `OverloadedStrings`. Now in
`gitHubCommitAction` we take the database connection as an argument,
select a row (for now it's always the first one to test this out), and
send back a GitHub commit URL (if a record matched, otherwise return an
empty string). Eventually we'll want to make this more real by selecting
the row corresponding to the channel in `message`. Also, instead of
returning an empty string, we should be returning an `Either`, so the
error state is clear.
Update the `ChannelRepoUrl` data constructor to use record syntax so we
can name its fields.
|
|
Have `PluginAction` functions take a Message type instead of a plain
string. This gives us access to additional fields on the message:
channel and nick.
sorbot.cabal:
Add `Message` to `exposed-modules` in order to be able to use it when
building.
Lib.hs:
Change our test message to be a `Message` data type to conform to the new
`PluginAction` interface.
Plugin.hs:
Use `Message` where appropriate. When calling `perform`, pass it a
`Message` instead of a `String`. This means we have to match the regex
within the plugin in order to get the match data. The benefit of that
change is that now we have access to the full message in the plugin if
we need it, not just the regex-filtered part.
GitHubCommit.hs:
Do a regex match against the Message text in order to get the SHA we
want from the message.
|
|
Extract the GitHub commit plugin code from "Plugin.hs" into its own
module. Now that we have things more set up and working to a certain
degree, we can split the code out.
|
|
Move our base plugin types to a new module to enable us to use them in
both the plugin matching code (which we'll leave in "Plugin.hs") and in
specialised plugin modules.
This enables us to import and provide a list of plugins in `plugins` in
"Plugin.hs" and use these types in that file and in the plugin files
without any circular/recursive module dependencies.
|
|
Take our initial experiment on the regex matcher from before and expand
it into the beginnings of a plugin architecture.
Add a new `Plugin` module. This exports a `Plugin` data type that
collects a regex and a function together. The idea will be to create a
list of plugins, and chat messages will get matched against the regexes
in that list. If the regex matches, the associated function will be run.
If the function produces output, that output should eventually be sent
back to the chat as a message.
Implemented `Show` on `Plugin` manually because the `String -> String`
function can't be derived. Decided to forego that part in the output and
only show the regex when printing.
Ended up with a little redundancy here in the functions for matching
plugins. The `realMatchPlugin` function needs to be renamed, just called
it that for now because I had started with `matchPlugin`, and only later
realised that its interface wasn't what I needed when calling it from
the `Lib` module.
Need to add some doc comments, but figured I'd commit what I have now
since it sort of works.
The `firstPlugin` function is only used by `matchPlugin`, so now I
realise it should probably be a local `where`-defined function.
Also thought the `String -> String` wasn't very descriptive. We'll want
to make a type alias for that that tells people this is a plugin
function.
Added a test plugin inline in this file for now. Eventually our plugins
should be stored in separate files in a "Plugins" directory (or
"Plugin", depending on how the module system works, will have to look
into that). For now, the program outputs the correct string created by
`gitHubCommitAction`, which is actually pretty cool. It's not actually
the really correct string, as the `match` variable is the regex instead
of the matched part of the test string, but close enough for now. We'll
go back and correct that momentarily.
Needed to add `Plugin` to the `exposed-modules` section in the `library`
build metadata in order to properly build the code. Was getting this
error about it:
Warning: The following modules should be added to exposed-modules or other-modules in /Users/tw/Documents/Development/sorbot/sorbot/sorbot.cabal:
- In the library component:
Plugin
Missing modules in the cabal file are likely to cause undefined reference errors from the linker, along with other problems.
Really liking this so far!
|
|
* Add the 'regex-tdfa' library for RegEx handling
* Experiment with matching a Git SHA
|
|
Apparently you can't write in any old string into the license field, so
my SPDX-formatted license caused the build to fail.
It was a bit difficult as a complete beginner to figure out how I was
supposed to format the version given the `(Maybe Version)` in the
documentation:
https://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-License.html#t:License
but fortunately a search through some open source Cabal files on GitHub
revealed the answer. Disappointing, though, that I can't declare the `+`
part of the license in an officially supported capacity, but okay.
|
|
|
|
$ stack new sorbot new-template
GHC v8.0.2
|