Skip to content
This repository has been archived by the owner on Apr 23, 2024. It is now read-only.

Commit

Permalink
V1.0.0 (#10)
Browse files Browse the repository at this point in the history
* Init unit test
Added TryRemove method and ContainsTask to CronScduler
Moved LastRunTimestamp to ICrobJob and implemented in run functions

* Added Enable and Disable crontab jobs to console commands

* Changed console commands output order

* Added remove job when file is deleted.

* Updated docs

* Updated jobs.md and README.md
Added config_json.md

* updated README.md for logo

* Changed schema for signers; moved to wallet section
Updated docs

* Changed namespaces
Added Next Run to console display
  • Loading branch information
cschuchardt88 authored Sep 18, 2023
1 parent ce29144 commit d4795f7
Show file tree
Hide file tree
Showing 41 changed files with 642 additions and 288 deletions.
12 changes: 11 additions & 1 deletion ALL.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3002EB89-067
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{E6DF2325-582C-4859-B3ED-8887A3326D3D}"
ProjectSection(SolutionItems) = preProject
docs\jobs.md = docs\jobs.md
docs\CONFIG_JSON.md = docs\CONFIG_JSON.md
docs\JOBS.md = docs\JOBS.md
README.md = README.md
EndProjectSection
EndProject
Expand All @@ -19,6 +20,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{6A
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crontab", "src\Crontab\Crontab.csproj", "{1B500F7A-36A3-42C2-9A77-470EDB25B638}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4DB93B25-158A-40C8-A19D-F2E5E4409B15}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crontab.Test", "tests\Crontab.Test\Crontab.Test.csproj", "{01DAE7D5-28B9-410E-8216-0A5421E924D0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -29,12 +34,17 @@ Global
{1B500F7A-36A3-42C2-9A77-470EDB25B638}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B500F7A-36A3-42C2-9A77-470EDB25B638}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B500F7A-36A3-42C2-9A77-470EDB25B638}.Release|Any CPU.Build.0 = Release|Any CPU
{01DAE7D5-28B9-410E-8216-0A5421E924D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01DAE7D5-28B9-410E-8216-0A5421E924D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01DAE7D5-28B9-410E-8216-0A5421E924D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01DAE7D5-28B9-410E-8216-0A5421E924D0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1B500F7A-36A3-42C2-9A77-470EDB25B638} = {3002EB89-0674-4B08-B155-353140FCCFCD}
{01DAE7D5-28B9-410E-8216-0A5421E924D0} = {4DB93B25-158A-40C8-A19D-F2E5E4409B15}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5ED09B52-78F7-49E0-8A0B-1A196F12118B}
Expand Down
59 changes: 47 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,65 @@
[![Static Badge](https://img.shields.io/badge/license-MIT-green)](/LICENSE)
![GitHub tag (with filter)](https://img.shields.io/github/v/tag/cschuchardt88/neo-cron-plugin)
![GitHub all releases](https://img.shields.io/github/downloads/cschuchardt88/neo-cron-plugin/total)

<p align="center" width="100%">
<img src="https://github.com/cschuchardt88/neo-cron-plugin/blob/master/imgs/logo-transparent.png" alt="Crontab-logo" />
</p>

```bash
* * * * * Crontab Expression Chart
│ │ │ │ │
│ │ │ │ │
│ │ │ │ |_________ Day of Week (0 – 6) (0 is Sunday)
│ │ │ |____________ Month (1 – 12), * means every month
│ │ |______________ Day of Month (1 – 31), * means every day
|________________ Hour (0 – 23), * means every hour
|___________________ Minute (0 – 59), * means every minute
```

<p align="center" width="100%">
<a href="https://github.com/cschuchardt88/neo-cron-plugin/blob/master/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-green" alt="license-MIT" />
</a>
<a href="https://github.com/cschuchardt88/neo-cron-plugin/tags">
<img src="https://img.shields.io/github/v/tag/cschuchardt88/neo-cron-plugin" alt="neo-cron-plugin-tags" />
</a>
<a href="https://github.com/cschuchardt88/neo-cron-plugin/releases">
<img src="https://img.shields.io/github/downloads/cschuchardt88/neo-cron-plugin/total" alt="neo-cron-plugin-releases-downloads" />
</a>
</p>

# neo-cron-plugin
Crontab task scheduler for executing blockchain tasks.
Task scheduler for sending transactions to the blockchain. Just as the
name implies `Crontab` does just that! Schedule jobs to invoke contracts
or transfer funds at certain times of the day, month, year, hour and
minute.

## Features
- Crontab Scheduler
- Invoke Contracts
- Invoke Transfers (_Nep17_)
- Task Scheduler
- Manage jobs in `cli` console.
- Send transaction types.
- Invoke Contract Methods
- Send Nep-17 Transfers

## Upcoming Features
- Send `VM` scripts in transactions.
- Detailed error reporting.
- enable/disable jobs in their config file.

Have a feature you want to recommend for this project. Just create an
[issue](https://github.com/cschuchardt88/neo-cron-plugin/issues).

# Install
This plugin requires at least `neo-cli` version
[3.6.0](https://github.com/neo-project/neo-node/releases). After you
download and extract the `.zip` file.

**Next Steps**
1. _Open `neo-cli` directory._
1. _Create a folder in the `Plugins` directory called `Crontab`._
1. _Copy & Paste `Crontab.dll`, `config.json` and `NCrontab.dll` into `Plugins\Crontab` directory._
1. _Edit `config.json` with your configuration. [More datails](/docs)_
1. _Edit `config.json` with your configuration. [More details](/docs/CONFIG_JSON.md)_

# Example Tasks
You can find more datails on how to configure [jobs here](/docs/jobs.md).
You can find more details on how to create and configure jobs [here](/docs/JOBS.md).

- Invoke a [contract](/examples/HelloInvokeMethod.job)
- Invoke a [transfer](/examples/HelloTransfer.job)
**Schedule Job Examples**
- [contract](/examples/HelloInvokeMethod.job)
- [transfer](/examples/HelloTransfer.job)
21 changes: 21 additions & 0 deletions docs/config_json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Config File
Is placed in the same directory as `Crontab.dll`. Located in `Plugins\Crontab`
directory.

## Root Section
| Property | Type | Description |
| ---: | :---: | :--- |
|PluginConfiguration|object|note: Standard for `NEO` plugins|

## PluginConfiguration Section
| Property | Type | Description |
| ---: | :---: | :--- |
|Network|uint32|Network you want to execute tasks on.|
|MaxGasInvoke|int64|Max gas allow on the `NEO` `VM`.|
|Job|object|see [job](#job-section) section for more details.|

## Job Section
| Property | Type | Description |
| ---: | :---: | :--- |
|Path|string|Where your `*.job` files are. Defaults to `jobs` folder in the `neo-cli` root directory. _note: You must create this folder_.|
|Timeout|uint32|Max time in seconds that a job can run for. It's recommended that it should be no more than 30 seconds.
67 changes: 48 additions & 19 deletions docs/jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
In this section you will learn how to create and configure job(s).

## Create a Job
Add `*.job` file to the path of `JobsPath` in the _config.json_. In this
location the plugin will pull all files; including ones from other
directories.
Create a create file with any name with `.job` extension. Place file in
`JobPath` location directory from [_config.json_](/docs/config_json.md) file.


## Template (_*.job_) File
## Basic Template (_*.job_) File
```json
{
"Type": "",
Expand All @@ -27,16 +25,46 @@ directories.
}
```

## Job Config
## Transfer Template (_*.job_) File
```json
{
"Type": "Transfer",
"Name": "",
"Expression": "",
"RunOnce": false,
"Wallet": {
"Path": "",
"Password": "",
"Account": ""
},
"Transfer": {
"AssetId": "",
"SendTo": "",
"SendAmount": 0.00,
"Comment": null,
"Signers": []
}
}
```

## Root Section
| Property | Type | Description |
| ---: | :---: | :--- |
|Type|enum|`Basic` or `Transfer`|
|Name|string|is the name of the job.|
|Expression|string|is the crontab expression.|
|RunOnce|boolean|Only execute the job once.|
|Name|string|Name of the job.|
|Expression|string|Crontab schedule expression. In `5` or `6` part format.|
|RunOnce|boolean|**Only** execute the job once.|
|Contract|object|See [details](#contract-section).|
|Wallet|object|See [details](#wallet-section).|

## Transfer Section
| Property | Type | Description |
| ---: | :---: | :--- |
|AssetId|string|Smart contract hash. As `ScrtipHash` format.|
|SendTo|string|**NEO** Wallet address. As `ScriptHash` format|
|SendAmount|decimal|Non-negative number.|
|Comment|string|`data` field in a transfer method. Unsure? Leave blank.|

## Contract Section
| Property | Type | Description |
| ---: | :---: | :--- |
Expand All @@ -47,15 +75,16 @@ directories.
## Wallet Section
| Property | Type | Description |
| ---: | :---: | :--- |
|Path|string|file path plus filename with extension. _note: path is relative to where file neo-cli.exe_|
|Path|string|Full file name path. Path is relative to where `neo-cli.exe` is.|
|Password|string|Password to open wallet file.|
|Account|string| `hash160` of the account in the wallet. _note: Transactions will preformed with this account._|
|Account|string|`hash160` of the account in the wallet. Transactions will be preformed with this account as sender and witness.|
|Signers|Array|An Array of strings as ScriptHash `hash160`. Defaults to wallet account.|

## Contract Parameters
| Property | Type | Description |
| ---: | :---: | :--- |
|Type|enum| see [details](#contract-parameter-enum).|
|Value|string| string format of the value type.|
|Type|enum|see [details](#contract-parameter-enum).|
|Value|string|string format of the value type.|

## Contract Parameter Enum
Arrays and Maps are not currently supported. That kind of data
Expand All @@ -66,9 +95,9 @@ per `byte` down for transactions.
| ---: | :---: | :--- |
|ByteString|`Base64`||
|Signature|`Base64`||
|Boolean|`Bool`|`True` or `False`|
|Integer|`BigInt`||
|String|`utf-8`||
|Hash160|`string`|_note: valid? depends if contract checks for validation_.|
|Hash256|`string`||
|PublicKey|`hexstring`||
|Boolean|`Boolean`|Value must be `True` or `False` in string form.|
|Integer|`BigInt`|**Only** `numbers` in string form up to 32 characters long.|
|String|`utf-8`|Make sure you use `utf-8` text encoding.|
|Hash160|`hexstring`|`ScriptHash` format with prefix `0x`|
|Hash256|`hexstring`| `SHA-256` hash starting with prefix `0x`.|
|PublicKey|`hexstring`|`ECPoint` `Secp256r1` public key in hex format with no prefix.|
3 changes: 2 additions & 1 deletion examples/HelloInvokeMethod.job
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"Wallet": {
"Path": "wallets/main.json",
"Password": "main",
"Account": "0x2dfa6249a7b8ee132370f0385165339982e52e53"
"Account": "0x951bf95707bb92a0c89d4308095697af60474472",
"Signers": []
},
"Contract": {
"ScriptHash": "0x0313bdad26721ae6867df516139d4e52f749f360",
Expand Down
12 changes: 5 additions & 7 deletions examples/HelloTransfer.job
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
"Wallet": {
"Path": "wallets/main.json",
"Password": "main",
"Account": "0x2dfa6249a7b8ee132370f0385165339982e52e53"
"Account": "0x951bf95707bb92a0c89d4308095697af60474472",
"Signers": []
},
"Transfer": {
"AssetId": "0x0313bdad26721ae6867df516139d4e52f749f360",
"SendTo": "0x2dfa6249a7b8ee132370f0385165339982e52e53",
"AssetId": "0xd2a4cff31913016155e38e474a2c06d08be276cf",
"SendTo": "0x495df01c83bfa34cf6e4cc904391c5d129ccbfe0",
"SendAmount": 0.001,
"Comment": null,
"Signers": [
"0x2dfa6249a7b8ee132370f0385165339982e52e53"
]
"Comment": null
}
}
Binary file added imgs/crontab-color-palette.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/fonts/Monofett Regular.ttf
Binary file not shown.
Binary file added imgs/logo-red.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added imgs/logo-transparent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 58 additions & 31 deletions src/Crontab/CronPlugin.Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,90 @@
// the main directory of the project for more details.

using Neo.ConsoleService;
using Neo.Plugins.Crontab.Jobs;
using Neo.Plugins.Crontab.Settings;

namespace Neo.Plugins.Crontab;

public partial class CronPlugin
{
[ConsoleCommand("crontab jobs list", Category = "Crontab Commands", Description = "List all the crontab jobs.")]
[ConsoleCommand("crontab disable", Category = "Crontab Commands", Description = "Disables a crontab job from running.")]
private void OnDisableTask(string jobId)
{
_ = Guid.TryParse(jobId, out var id);
if (_scheduler.Entries.TryGetValue(id, out var jobEntry) == false)
ConsoleHelper.Error($"Could not find the crontab job with id {jobId:n}.");
else
{
jobEntry.IsEnabled = false;
ConsoleHelper.Info("", $"Disabled ", $"{id:n}", " successfully.");
}
}

[ConsoleCommand("crontab enable", Category = "Crontab Commands", Description = "Enables a crontab job to run its schedule.")]
private void OnEnableTask(string jobId)
{
_ = Guid.TryParse(jobId, out var id);
if (_scheduler.Entries.TryGetValue(id, out var jobEntry) == false)
ConsoleHelper.Error($"Could not find the crontab job with id {jobId:n}.");
else
{
jobEntry.IsEnabled = true;
ConsoleHelper.Info("", $"Enabled ", $"{id:n}", " successfully.");
}
}

[ConsoleCommand("crontab list", Category = "Crontab Commands", Description = "List all the crontab jobs.")]
private void OnListCrontabJobs()
{
if (_scheduler.Entries.Any() == true)
ConsoleHelper.Info("---------", "Jobs", "---------");
ConsoleHelper.Info("--------------------------------------------");

foreach (var job in _scheduler.Entries)
foreach (var entry in _scheduler.Entries)
{
ConsoleHelper.Info(" ID: ", $"{job.Key:n}");
ConsoleHelper.Info(" Name: ", $"\"{job.Value.Settings.Name}\"");
ConsoleHelper.Info("Expression: ", $"{job.Value.Settings.Expression}");
ConsoleHelper.Info(" RunOnce: ", $"{job.Value.Settings.RunOnce}");
if (job.Value.LastRunTime != default && job.Value.LastRunTime > CronScheduler.PrecisionMinute())
ConsoleHelper.Info(" RunNext: ", $"{job.Value.Schedule.GetNextOccurrence(DateTime.Now):MM/dd/yyyy hh:mm tt}");
ConsoleHelper.Info(" ID: ", $"{entry.Key:n}");
ConsoleHelper.Info(" File: ", $"{entry.Value.Settings.Filename}");
ConsoleHelper.Info(" Name: ", $"{entry.Value.Settings.Name}");
ConsoleHelper.Info(" Schedule: ", $"{entry.Value.Settings.Expression}");
ConsoleHelper.Info(" Enabled: ", $"{entry.Value.IsEnabled}");
ConsoleHelper.Info(" Run Once: ", $"{entry.Value.Settings.RunOnce}");
if (entry.Value.Job.NextRunTimestamp != default)
ConsoleHelper.Info(" Next Run: ", $"{entry.Value.Job.NextRunTimestamp:MM/dd/yyyy hh:mm tt}");
else
{
if (job.Value.LastRunTime != default)
ConsoleHelper.Info(" RunLast: ", $"{job.Value.LastRunTime.ToLocalTime():MM/dd/yyyy hh:mm tt}");
else
ConsoleHelper.Info(" RunLast: ", $"Processing...");
}
ConsoleHelper.Info(" Next Run: ", $"N/A");
if (entry.Value.Job.LastRunTimestamp != default)
ConsoleHelper.Info(" Last Run: ", $"{entry.Value.Job.LastRunTimestamp.ToLocalTime():MM/dd/yyyy hh:mm tt}");
else
ConsoleHelper.Info(" Last Run: ", $"N/A");

ConsoleHelper.Info(" Filename: ", $"\"{job.Value.Settings.Filename}\"");
if (job.Value.Settings.GetType() == typeof(CronJobBasicSettings))
if (entry.Value.Settings.GetType() == typeof(CronJobBasicSettings))
{
var contractSettings = job.Value.Settings as CronJobBasicSettings;
var contractSettings = entry.Value.Settings as CronJobBasicSettings;
ConsoleHelper.Info("", "-------", "Contract", "-------");
ConsoleHelper.Info("ScriptHash: ", $"{contractSettings.Contract.ScriptHash}");
ConsoleHelper.Info(" Method: ", $"{contractSettings.Contract.Method}");
ConsoleHelper.Info("Parameters: ", $"[{string.Join(", ", contractSettings.Contract.Params.Select(s => $"\"{s.Value}\""))}]");
}
else if (job.Value.Settings.GetType() == typeof(CronJobTransferSettings))
else if (entry.Value.Settings.GetType() == typeof(CronJobTransferSettings))
{
var transferSettings = job.Value.Settings as CronJobTransferSettings;
var transferSettings = entry.Value.Settings as CronJobTransferSettings;
ConsoleHelper.Info("", "-------", "Transfer", "-------");
ConsoleHelper.Info(" AssetId: ", $"{transferSettings.Transfer.AssetId}");
ConsoleHelper.Info(" To: ", $"{transferSettings.Transfer.SendTo}");
ConsoleHelper.Info(" Asset Id: ", $"{transferSettings.Transfer.AssetId}");
ConsoleHelper.Info(" Send To: ", $"{transferSettings.Transfer.SendTo}");
ConsoleHelper.Info(" Amount: ", $"{transferSettings.Transfer.SendAmount}");
ConsoleHelper.Info(" Signers: ", $"[{string.Join(", ", transferSettings.Transfer.Signers.Select(s => $"\"{s}\""))})]");
ConsoleHelper.Info(" Data: ", $"\"{transferSettings.Transfer.Comment}\"");
ConsoleHelper.Info(" Comment: ", $"{transferSettings.Transfer.Comment}");
}
ConsoleHelper.Info("", "--------", "Wallet", "--------");
ConsoleHelper.Info(" Path: ", $"\"{job.Value.Settings.Wallet.Path}\"");
ConsoleHelper.Info(" Account: ", $"{job.Value.Settings.Wallet.Account}");
ConsoleHelper.Info(" Path: ", $"{entry.Value.Settings.Wallet.Path}");
ConsoleHelper.Info(" Account: ", $"{entry.Value.Settings.Wallet.Account}");
ConsoleHelper.Info(" Signers: ", $"[{string.Join(", ", entry.Value.Job.Signers.Select(s => $"\"{s.Account}\""))}]");

if (_scheduler.Entries.Count > 1)
ConsoleHelper.Info();
ConsoleHelper.Info("--------------------------------------------");
}

if (_scheduler.Entries.Any() == true)
ConsoleHelper.Info("----------------------");
if (_scheduler.Entries.Count == 1)
ConsoleHelper.Info("--------------------------------------------");

ConsoleHelper.Info(" Total: ", $"{_scheduler.Entries.Count}", " job(s).");
ConsoleHelper.Info("", "Total: ", $"{_scheduler.Entries.Count}", " job(s).");
}
}
Loading

0 comments on commit d4795f7

Please sign in to comment.