Cron - asynchronous automation
The name of this module might sound familiar to some of you coming from the Linux(Unix) background. That is, indeed, because this module draws its inspiration from a daemon named cron
. Sharing some similarities with it, this module adds some more functionality to the regular cron
syntax.
In general, the cron
module provides a way to execute commands in a particular time or according to a particular event.
All of the event types below can be created using the &event
command (assuming that &
is the prefix)
Cronjobs
A cronjob is a syntactic construction that tells cron that a certain command should be executed at a given time. There are 2 valid syntax forms for a cronjob:
- The generic cronjob syntax
+---------- minute (0-59)
| +-------- hour (0-23)
| | +------ day of month (1-31)
| | | +---- month (1-12)
| | | | +-- day of week (1-7)
| | | | |
v v v v v
n n n n n command
Every n is either a number, an asterisk (*
), a modulo predicate(*/n
) or a list (n,n,n,...
) (see Appendix 1)
- The single use syntax
DD.MM.YY HH:MM command
(Note: HH:MM DD.MM.YY
is also valid, as well as DD.MM.YYYY HH:MM
)
DD.MM.YY is a date format, with day, month and year separated by a dot. HH:MM is a 24-hour time format.
Event directives
As with the modern cron
implementations on Linux(Unix), there are multiple "directives" for cron. Unlike these implementations, directive syntax is a bit more complex, allowing matching against certain arguments passed along with the directive.
Directive example
Rather than explaining the directives from scratch, let's look at a practical example:
@message /%sGNU\/Linux%s/ >913818433748086784 : &echo *funni stallman bad copypasta goes here by $USERNAME*
@message /%sGNU\/Linux%s/ <913818433748086784 : &echo *funni stallman bad copypasta goes here by $USERNAME*
This might look very confusing to those of you who are used to the @reboot
directive in cron. But in fact, it's not unlike that directive - it's just that after the @message
directive we have 2 arguments that are being matched. In this case, it's the message itself and the message author's id. To separate the arguments list from the command, we use a colon :
delimiter. Now, let's look at what each argument does:
/%sGNU\/Linux%s/
is a Lua regex pattern that matches every message containing a space-separated string "GNU/Linux".>913818433748086784
(<913818433748086784
) is a numeric comparison pattern that takes a string, converts it to a number (if possible), and then compares it to the pattern (so for example if our bot's id is "913818433748086784", these 2 directives will match any user id that is greater or lesser than the numeric value of the bot's id (practically, it means that it will match any id that isn't the bot's id))
We finalize the argument list with :
. Note that it should be separated from both sides with spaces.
There is also a channel id argument for message, but since the directives don't specify any patter to match it against, this argument is simply ignored. For any given directive, any amount of arguments can be ignored.
Last but not least, directives can pass certain arguments to the command itself as environment variables. In our example, $USERNAME
is an environment variable. Those of you who are knowledgeable about the ways of Unix shells already know what this means. Those of you who don't might want to read the next paragraph.
Environment variables
Most of the events that are currently available pass some environment variables to the command, which can be used to manipulate the command. For example, if a @message
event is triggered, the $USERNAME
environment variable is changed to the username of the message author. All environment variables are prefixed with a dollar sign $
and can only be used inside of the command.
Argument matching patterns
There are multiple patterns available, each one can be applied to any argument:
/.../
is a regex pattern that matches the argument against a Lua-style regular expression (See Programming in Lua)>n
,<n
,>=n
,<=n
is a numeric comparison pattern that converts argument to a numeric value (if possible) and then compares it against numbern
n
is a separate numeric comparison pattern that converts argument to a numeric value and checks if both the argument and the numbern
are equal."..."
and'...'
are both string comparison patterns that check that the argument matches against the string enclosed in the quotes.*
is a pattern that matches any argument. It's a short form of the regex pattern/.*/
.
Note that while both "..."
and n
are equivalent when used against numbers, n
will fail to compare non-numeric arguments because it converts the argument to a number.
Available directives
Currently, the directives list is very short, but as updates will come along, this list will expand.
Format is @<directive name> <argument> <argument2> ... <$VARIABLE> <$VARIABLE2> <VARIABLE3>
- @message: content, userId, channelId, $USER, $USERNAME, $CHANNEL, $CONTENT
- @messageOnce: content, userId, channelId, $USER, $USERNAME, $CHANNEL, $CONTENT
- @ban: userId, $USER, $USERNAME
- @banOnce: userId, $USER, $USERNAME
- @unban: userId, $USER, $USERNAME
- @unbanOnce: userId, $USER, $USERNAME
- @join: userId, username, age, $USER, $USERNAME, $AGE, $DISCRIM, $TAG
- @joinOnce: userId, username, age, $USER, $USERNAME, $AGE, $DISCRIM, $TAG
- @leave: userId, username, role, $USER, $USERNAME, $AGE, $DISCRIM, $TAG, $GUILDTIME, $ROLE
- @leave: userId, username, role, $USER, $USERNAME, $AGE, $DISCRIM, $TAG, $GUILDTIME, $ROLE
Argument description:
content
- Message content.userId
- Discord ID of the user that triggered the event.username
- Discord username of the user that triggered the event.age
- Discord account age in seconds.role
- Primary role ID of the discord user that triggered the event.channelId
- ID of the channel where the event was triggered.
Environment variable description:
$USER
- User ID that triggered the event.$USERNAME
- Username that triggered the event.$AGE
- Discord account age in seconds.$DISCRIM
- Discriminator (last 4 digits) of a full Discord tag.$TAG
- Full discord tag.$GUILDTIME
- Time spent in the guild in seconds.$ROLE
- Primary role of the user.$CHANNEL
- Channel where the event occured.
Examples
- Printing a bump reminder every 2 hours
* */2 * * * &echo bump
- Printing a one-time reminder to yourself
15:40 10.09.22 &pingself important stuff
- Watching people say bad words
@message /^[oO][vV][hH]$/ : &message Somebody said a bad word!
Delay command
There is one special type of command that utilizes the cron system - the &delay
command.
It recognizes time delay in format <number><time unit>
, where time unit is one of the following:
h
- hourm
- minuted
- dayw
- weeky
- year
As an example, to execute a command after 2 days, 1 week and 6 hours, one could write the following:
&delay 2d1w6h &command
or
&delay 1w6h2d &command
Appendix 1.
In the Linux(Unix) cron
syntax there exist multiple syntactic facilities for matching a range of time.
*
matches every number possible (so, if it's in place ofmonth
, the command would execute every month)*/n
matches ever n-th number (if it's in place ofminute
with n=30, then the command will execute every time the minute value is evenly divisble by 30 (NOTE: While in this case the command will indeed execute every 30 minutes, this does not mean that for n=45 it will execute every 45 minutes. Instead, it will only execute whenminute
value is exactly 45.))n,n,n
matches any of the numbers that are in the comma-separated list of numbers (if we have a list14,16,18
in place ofhour
, the command will execute at 2 PM, 4 PM and 6 PM, respectively.)