Coder Social home page Coder Social logo

cbot's People

Contributors

brenns10 avatar mtbentley avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

mtbentley idkunal

cbot's Issues

Add more functionality to plugin system

Currently, plugins consist of a single function loaded from a shared object. This function registers all handlers. The bot itself doesn't know which handlers belong to which plugin, or how to safely unload the plugin. The bot also can't query the plugin for information such as help text, etc. A better system would be for a plugin to (optionally) populate a struct of function pointers at registration time. Further, a plugin should receive a pointer to some sort of bookkeeping struct (e.g. struct cbot_plugin) which it can use to store a pointer to private data.

High level goals:

  1. Add a mechanism to retrieve help information
  2. Add configuration to each plugin's registration
  3. Add a mechanism to unregister an active plugin, removing all registered handlers and shutting down all lwts
  4. (Stretch) make it possible to load the same plugin .so multiple times with different configuration

Rewrite karma on sqlite

Once #9 is implemented, we should make the karma plugin store data in sqlite. This may actually be easier since it will handle a lot of the logic of sorting and updating for us. Plus, it will mean that karma can survive a cbot restart, which would be a momentous achievement.

Reconnections

Darwin IRC server reboots a lot to try new features and builds. It would be ideal if cbot could reconnect on server shutdown. This could be achieved in a bash while loop, but it would be nice to have some exponential backoff or something (which could still be done in bash, or C).

Tokenizing interface for plugins

Plugins will likely want to tokenize messages into a sequence of command arguments separated by whitespace. Create a common system to simplify plugins:

struct cbot_tok {
    char *original; // the original copied string, modified to tokenize
    char **tokens; // pointers into original for each token
    int ntok; // count of tokens in array
    int cap; // capacity of the token array
};

/**
 * Tokenize `msg` (copy it first) and fill `*result` with the tokens.
 * Return count of tokens (also stored in ntok) on success, negative on fail.
 */
int cbot_tokenize(const char *msg, struct cbot_tok *result);

/**
 * Frees resources held by `tokens`. Does not free the actual tokens struct.
 */
void cbot_tok_destroy(struct cbot_tok *tokens);

The tokenizing system would be defined as follows:

  1. If a token begins with any character other than a double quote ", the token will be delimited by the next whitespace character (\t or space ) or NUL. The next token will begin with the next non-whitespace character (if this token was not delimited by NUL).
  2. If a token begins with a double quote ", then the token will continue until the first matching double quote is found. The matching double quote MUST be followed by either a whitespace character or NUL, which will delimit the token. The token will contain all the text except the opening and closing double quote.
  3. Quoted tokens may include a literal double quote by "escaping" the quote, which means putting two double quotes in sequence. This will result in the token containing a single double-quote.
  4. While processing a quoted token, if a double quote is encountered, followed by a character other than whitespace or null (rule 2), or a second double quote (rule 3), then this will be considered an error.
  5. While processing a quoted token, if a NUL character is encountered, this will be considered an error.

Some examples (input string enclosed in brackets for simplicity, assume no brackets occur in the input strings):

INPUT: [arg1 arg2]
[0]: arg1
[1]: arg2

INPUT: ["hello world" 2]
[0]: hello world
[1]: 2

INPUT: [""]
[0]:  (empty string)

INPUT: []
No tokens found, zero length array.

INPUT: ["hello ]
Error, rule 5.

INPUT: [hello" world]
[0]: hello"
[1]: world

INPUT: ["hello"world]
Error, rule 4.

INPUT: ["he said ""hello"" to me"]
[0]: he said "hello" to me

Reasons for using this method:

  • No need to use backslash to escape itself and other characters. Limits the ability to use backslash escaping to insert characters which would be otherwise illegal in IRC, etc.
  • For the majority of cases, this allows readable specification of regular expressions, since backslashes need not be escaped, and quotes are rarely used.
  • By not allowing alternating between quoted and unquoted modes within the same token (as bash would), we simplify processing a lot.

Does not build on OpenBSD 6.9 -Stable

using meson build in the cbot directory eventually gives me this error:

meson.build:52:0: ERROR: C shared or static library 'dl' not found

Any solutions? things i am forgetting?

Plugin: weather

Get weather reports, probably from wttr.in or something similar and simple.

Logging Plugin

Simply writes timestamp, username, and message to CHANNEL-DATE.txt whenever it receives a message. No buffering or anything like that. RIP Hard Drive.

cron API

Multiple plugins have the ability to schedule a callback, and they usually do it by a month, day of month, weekday, etc. All cron stuff. I wonder if an API could be provided like:

void *cbot_callback_cron(plugin, callback, char *schedule);

Which would trigger the callback at the next matching time. The callback would need to re-register itself with the same expression to trigger it again.

Modular authentication mechanism

The hashchain thing was neat but maybe not terribly practical. I do think that here still needs to be a good way to ask "is a user trusted to execute some privileged command?". I think this should be modular and specified in the config. Some implementations:

  • Blindly trust a set of nicknames (bad)
  • Hash chain (more secure but highly impractical)
  • Trust nicknames if they are authenticated via nickserv (flaky, implementation-specific)
  • IRCv3 based options (need a new/improved libircclient)

Does not work.

./sample.cfg:10: syntax error (cbot config op read)lab@lab:~/git/cbot$

Plugin: notifications

My IRC "client/bouncer/whatever" of choice is thelounge. It doesn't have per-channel notification settings, which is a bit of a drag because there are a few channels where I would like to be pinged for most messages, but for most channels I would not like notifications. Add a plugin which allows you to register to be DM'd whenever there is a message which satisfies some conditions:

  • A message occurs in a particular channel
  • The last activity in that channel was more than N minutes ago
  • The message matches a regex
  • The message was sent by a user

Upon a message satisfying these conditions (stored in persistent db #9 ?), the plugin should DM the requestor to notify them.

Suggested command interface:

cbot notify add CHANNEL [inactive-for:[[HH:]MM:]SS] [regex:pattern] [from:nick]
-> NICK: added rule NUMBER
cbot notify list
-> NICK: rule NUMBER: <rule string using same syntax above>
cbot notify remove NUMBER
-> NICK: removed rule NUMBER
cbot notify update NUMBER <rule string using same syntax above>

This would depend on #9 for storing rules across restarts, and #10 for parsing commands in a foolproof way.

Bot can't trigger HEAR if message starts with its name.

This is a problem associated with the separation of HEAR and MSG events. They're not fundamentally different, and yet I separate them.

I think the resolution will be to get rid of the distinction and make sure regex matching is nice.

"greet" plugin will reply to greetings targeted to the wrong user

If two cbot instances, cbot and cbotdev are in a channel and a user says "hello cbot", both will reply. This is because their regex matches, but there is still more message after the end of the cbotdev match.

In regex matching, we should verify that the regex match length is length of the entire string.

Plugin: replies

There are several plugins whose entire purpose is to reply to some message with another message, possibly including a captured regex field, or an attribute such as the sender's nickname. As nice as it is to write some good old fashioned C code to achieve such tasks, we probably don't need that. Let's build a generic reply plugin, which loads its triggers and responses from the configuration file. Depends on #15 and #16 for configuration and improved plugin support.

Plugin: covid

cbot rona / cbot covid - ask questions about the latest covid data

Configuration file for bot and plugins

Currently we provide all bot configuration on the command line. I've begun creating launcher scripts which contain all the options, so functionally we're abusing bash scripts as configuration files.

This actually isn't awful; it has worked for a while. But we've reached the point where plugins should be able to have configuration of their own, and this can't be achieved with command line arguments.

Integrate with libconfig (or an INI parser) to allow cbot to accept a configuration file.

Asynchronous I/O threads

A while back I had fun implementing userspace threads. I'd like to wrap that into a library which implements threads and supports a poll-based event loop. The library would need to do the following:

  1. Most of what that repository does, but preferably using the ucontext api.
  2. Create an API to set a task to "runnable".
  3. Create an API for the current task to block waiting for an FD set to become ready. This will create an association of an FD with that thread, and the event loop / scheduler will use poll() when no tasks are available, to determine which tasks should resume running.
  4. (Maybe) create wrappers for common file operations which use the above API. They would first do O_NONBLOCK, and if they get EWOULDBLOCK, they block until their file descriptor becomes available. I say maybe, because I don't actually need file operations, and this would be considerable work.
  5. Create an API for the current task to "sleep" at a somewhat reduced granularity. The scheduler would maintain a list of timers in sorted order, and periodically (after poll timeouts, etc) check whether a task should be scheduled.
  6. Create an event loop. It would use poll() whenever no tasks are available, with a timeout set at a level which could support the timers above.
  7. Create an API to protect the current thread from being scheduled out. This could be used as a sort of an assertion, to be sure no "sleeping I/O" is called on a thread when it shouldn't sleep.

This would be enough from the library features.

The current cbot operation would be mostly unchanged. The cbot operations which occur in my code are mostly non blocking. The entire main bot would run on one thread. Plugins would get an API to create a thread (but there would be no way to retrieve its return value, or join it) so they can do I/O, but the I/O must be non-blocking (e.g. libcurl has a mode for this). Plugins could also use threads to run asynchronous operations by a timer.

Support multiple plugin instances

In #16 I listed out several things to improve the plugin mechanism. The big ticket items were done, but one thing not fully realized was the ability to load multiple instances of the same plugin from the same shared object.

This ticket is to ensure that this is supported. Update the configuration file syntax (hopefully in a backward compatible way) so that you can specify a plugin filename, thus multiple config entries mapping to the same .so file would be allowed. Then, ensure that all plugins are correct about not misusing global data/variables. Test out loading multiple instances with different configurations.

Integration test backend

I've begun using unity for unit testing. I could expand that quite a bit, but some functionality needs integration testing. A fake backend would be good for that. I'd probably use Python to write the tests, and I'd want flexibility in the integration testing system for:

  • trigger messages into any channel / group, from any user
  • provide configurations to plugins
  • provide sqlite data to plugins
  • make assertions on the output
  • mock HTTP requests made by curl
  • some way to simulate the passing of time to properly test callbacks?

Configurable builds

Meson is configurable enough, we should be able to specify which backends and plugins we want to build support for. We could get rid of libircclient when we don't need it, among other things.

It would also be interesting to see if we could have built-in plugins to eliminate the runtime dlopen(). I used dlopen() mostly for learning back in the day, but it serves no practical purpose, as we have no stable ABI for plugins.

Graceful shutdowns

Since the introduction of sc-lwt the event loop will continue running if there is at least one thread active. Unfortunately, there are multiple threads active now with the curl integration, so an IRC shutdown or EOF on stdin will not result in a graceful shutdown.

Need to implement a shutdown hook which backends call when they end their main loop, which can terminate all other threads (hopefully gracefully) and shut things down.

Persistent sqlite database

bot->privDb is a memory-only sqlite connection for tracking usernames and channel memberships, and other transient data. Create a sqlite database file and connection, and CLI argument to specify its location. Then, provide this database to the plugin API to allow plugins to have persistent state.

Help mechanism

In #16 I listed out several things to improve the plugin mechanism. The big ticket items were done, but one thing not fully realized was a help system.

Build a help command which reads the description field of each plugin's ops struct, and gives a list of those as output. Remove the old help plugin. Be sure to add descriptions to all plugins.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.