aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)Author
2017-08-17Update TODOTeddy Wing
Mark off recently completed tasks.
2017-08-17Message: Add a function to get the `text` field as a stringTeddy Wing
Clean up some of the calls to get the `text` field by adding a function that abstracts the call to `Data.Text(unpack)`.
2017-08-17Change `Plugin` and `Message` from `String` to `Data.Text`Teddy Wing
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.
2017-08-16handlePrivmsg: call out to plugins in QUERYTeddy Wing
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.
2017-08-16IRC.hs: Remove old commented codeTeddy Wing
This was there as a test but didn't work. It's no longer needed, so we can remove it.
2017-08-16IRC.hs: Handle `Nothing` branchesTeddy Wing
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.
2017-08-16handlePrivmsg: Move duplicated code to new functionTeddy Wing
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.
2017-08-16Remove commented code from a0d21d9aed3a38e98e622fbae5fcab3e91c5ddeeTeddy Wing
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.
2017-08-16TODO: Add a few new tasksTeddy Wing
2017-08-16Connect plugins to IRCTeddy Wing
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`.
2017-08-13Lib.hs: Move IRC-related code to a new "IRC" moduleTeddy Wing
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.
2017-08-13dispatchEvent: Ignore user's hostname argumentTeddy Wing
We don't need this information. I was just saving it to find out what that argument actually was.
2017-08-13Lib.hs: Bot now responds on the channel that message came fromTeddy Wing
Instead of always responding on a hard-coded channel, the bot now responds on the channel the PRIVMSG was received on. This can be either a regular channel or a query message. The `serv` argument is apparently the message sender's hostname. Wanted to print it out to see what that argument was. I had been following this example from "barrucadu"'s 'yukibot': https://github.com/barrucadu/yukibot/blob/31930b234eb423ed74546b56ada100105c1680ce/yukibot-backend-irc/Yukibot/Backend/IRC.hs#L152-L156 but the code ignored the first `Event` argument. In order to send it as a chat message, needed to convert it to a `Data.Text`, using the method courtesy of this tutorial: https://haskell-lang.org/tutorial/string-types
2017-08-13Lib.hs: Test chat message listening and postingTeddy Wing
Add an event handler that gets called when a message is posted to a channel the bot is on. When a message is posted, the bot will send a chat message containing the text "test". This tests out the message handling and posting mechanism, and gives us a place to build off of to allow the bot to communicate.
2017-08-12Lib.hs: Make `Network.IRC.Client` a qualified importTeddy Wing
Make it a bit more obvious where these functions are coming from by prefixing them with `IRC.`.
2017-08-12stack.yaml: Turn off `-support_aesni` when compiling 'cryptonite'Teddy Wing
The 'irc-client' package depends on 'cryptonite'. I was getting this compilation error from 'cryptonite' on my machine: [111 of 111] Compiling Crypto.Cipher.AES ( Crypto/Cipher/AES.hs, .stack-work/dist/x86_64-osx/Cabal-1.24.2.0/build/Crypto/Cipher/AES.o ) cc1: error: unrecognized command line option "-maes" `gcc' failed in phase `C Compiler'. (Exit code: 1) The package recommends disabling AESNI via a compile flag to get around this error (https://github.com/haskell-crypto/cryptonite#known-building-issues): cabal configure --flag='-support_aesni' Add 'cryptonite' to our `extra-deps` in order to be able to set a flag on it. Turn off the `-support_aesni` flag in order to facilitate compilation.
2017-08-12Set up basic IRC supportTeddy Wing
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.
2017-08-11gitHubCommitAction: Add missing period to error messageTeddy Wing
2017-08-11gitHubCommitAction: Fix whitespaceTeddy Wing
The `=` was previously aligned with the function definition below, but since the pattern match of the lower function definition changed, the `=`s were no longer aligned. Seems like it would be weird to move the `=` out and create a bunch of blank space to align the two `=`s, so moving the function body to the next line.
2017-08-11gitHubCommitAction: Ignore unused variableTeddy Wing
2017-08-11Lib.hs: Move `putStrLn` outside the `case` statementTeddy Wing
It's duplicated so I figured I'd take it out.
2017-08-10gitHubCommitAction: Get error handling to work using `Either`Teddy Wing
Get rid of the `fail` and use an `Either` for error handling instead. This allows us to send back the response string like before, but provide an additional error message when no data comes back from the database query. Remove the old error handling code I had tried to set up previously.
2017-08-10Try to set up error handling for GitHub Commit pluginTeddy Wing
This doesn't really work, but I wanted to hold onto this step. I've been reading about error handling, notably this article: http://www.randomhacks.net/2007/03/10/haskell-8-ways-to-report-errors/ I kind of like the idea of just using `fail`, and being able to have that set up some polymorphism to handle `Maybe`, `Either`, and `IO` situations, but today read some literature that discouraged using fail, since not all monads implement it. Anyway, this code doesn't print out the error string like I intended it to, so I guess I'll have to use `Either` instead.
2017-08-10Add TODOTeddy Wing
A few tasks to set up some clear next steps.
2017-08-03GitHubCommit.hs: Only select `repo_url` column from databaseTeddy Wing
We don't need to select the channel because we don't do anything with it. All we need is the repo URL. Create a new data type for use with the database query that allows us to select only the URL. Remove the `ChannelRepoUrl` type as it's no longer needed.
2017-08-03GitHubCommit.hs: Select row corresponding to correct channelTeddy Wing
Ask for the row corresponding to the channel received from the chat message. This gets rid of the previously hard-coded SQL and allows us to dynamically get the right URL value.
2017-08-02GitHubCommit.hs: Remove progress development commentsTeddy Wing
Added these while developing ff00355ade021d3e06d55017c5337f488474e5cb to test out different theories while trying to get the project to compile. These are no longer relevant any more, so delete them.
2017-08-02ChannelRepoUrl: Get rid of the `id` fieldTeddy Wing
We're not using it, and originally I didn't even put it in my SELECT query (which actually messed me up for a couple hours, as two columns in the SELECT does not match three columns in the data type). Added it later as a fix for that problem. But since I don't really want or need to select the id, we shouldn't bother, and in order to do so, we need to update the Haskell data type to only include two fields, so sqlite-simple can correctly convert the data.
2017-08-02Connect plugins to the databaseTeddy Wing
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.
2017-08-02Plugin.hs(performPlugin): Use `PluginAction` typeTeddy Wing
Instead of manually writing out the type of the action function returned from `performPlugin`, use the consolidated `PluginAction` type. This makes it easier for us to change the `PluginAction` type signature if necessary.
2017-08-02GitHubCommit.hs: Make types of `ChannelRepoUrl` more specificTeddy Wing
Define some extra types in `ChannelRepoUrl` because I didn't like the generic ones.
2017-08-02GitHubCommit.hs: Add `ChannelRepoUrl` database typeTeddy Wing
A type that will be used in conjunction with 'sqlite-simple' to read values from the plugin's database table.
2017-08-02Change `PluginAction` type to `Message -> String`Teddy Wing
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.
2017-08-02Plugin/Base.hs: Remove duplicated exportsTeddy Wing
When I added the `Plugin(..)` export, I didn't think to remove the exports for the functions created by the Plugin record data type. Didn't see the warnings from the compiler when building until now.
2017-08-02Lib.hs: Remove unused `Text.Regex.TDFA` importTeddy Wing
We previously removed the call to `=~` that we needed this import for in 4bb65c50a2a85404af6d122acc53b6fb1739652b.
2017-08-01Add `Message` typesTeddy Wing
Add a few types to collect properties of a message, including message body, channel posted to, and sender. Need something like this for the GitHub Commit plugin, where we need access to the message to get the SHA, and we need the channel to get the repo URL from the database.
2017-08-01.gitignore: Ignore local Sqlite databaseTeddy Wing
And add comments and sections.
2017-08-01Remove old hand-written migrationsTeddy Wing
Remove these migrations that I had written before installing 'dbmigrations'. These have been replaced by the one defined in 70b3605a346b746273a79c1a3ff4bc40ae0218bf.
2017-08-01Add migration for GitHub Commit plugin tableTeddy Wing
A table that maps channel names with repo URLs in order to be able to build a commit URL. Previously committed in d1bdc16c9b1ad9a2b17b90a998640ebad1c02f9a, but now using the 'dbmigrations' format.
2017-08-01Add moo configuration for 'dbmigrations-sqlite'Teddy Wing
Provide the `moo` command with the proper configuration needed in order to run.
2017-08-01Install 'dbmigrations-sqlite'Teddy Wing
Use this for database migrations. Seems to be the right tool for database migrations in Haskell. Originally I had thought about doing it manually with 'up' and 'down' SQL files and some kind of Makefile or script to run them, but then realised that, of course, you have to keep track of which migration you're on, and figured, well, might want to outsource that to another program. I had originally written my migration in Mark Bates' pop/soda format, but will have to convert that for 'dbmigrations'. Thankfully got a little help from https://github.com/basti1302/elm-lang-de#development-setup on how to actually install the 'dbmigrations-sqlite' package using Stack. Put the installation command into a new "setup.sh" file to make it easily repeatable. Needed to add a bunch of lines to the `extra-deps` section of "stack.yaml" in order to be able to properly install the package.
2017-07-31Add database migrations for GitHub Commit pluginTeddy Wing
Create a table to store mappings between channels and repo URLs. We don't have any database configuration set up or anything to run the migrations yet, but this gets the schema defined.
2017-07-30Plugin.hs: Add some minimal documentationTeddy Wing
Write some short documentation above the function definitions to remind myself later about what they're supposed to do.
2017-07-30Move GitHub Commit plugin to its own moduleTeddy Wing
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.
2017-07-30Move `Plugin` and `PluginAction` to a new moduleTeddy Wing
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.
2017-07-30Add a type for plugin actionsTeddy Wing
Instead of implicitly using `String -> String` as the type for plugin action/perform functions, create a real type to represent this.
2017-07-30Get rid of `realMatchPlugin`Teddy Wing
I didn't even need the `matchPlugin` that took `plugins` as an argument, I can just use the `plugins` function directly in `matchPlugin`. Get rid of `realMatchPlugin` because that was just a temporary name until I got things working. Move `firstPlugin` into the definition of `matchPlugin` because it's not needed anywhere else and is just used to pattern match for the first matched plugin in the list.
2017-07-30performPlugin: Use the correct match stringTeddy Wing
I had been using the regex instead of the match result. Do another regex match to correctly output our SHA instead of the match regex. I had tried putting the `=~` match in another function and using it from here and in the list comprehension in `matchPlugins`, but then found out that, of course, the return types didn't agree (`matchPlugins` needs a `Bool` and `performPlugin` needs a `String`). Ended up dispensing with the extra function and doing the match both times instead.
2017-07-30Transform regex match into the beginning of a plugin architectureTeddy Wing
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!
2017-07-30Restrict Git SHA regex to full SHAsTeddy Wing
Only match SHAs if they're the full 40 characters long. This will become important later when we have an actual chat bot and we don't want to match on partial SHAs.