-
Notifications
You must be signed in to change notification settings - Fork 0
/
todoist-helper.php
executable file
·281 lines (246 loc) · 10.3 KB
/
todoist-helper.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
<?php
// Todoist API construct strings
define("API_BASE", "http://todoist.com/API");
define("APIS_BASE", "https://todoist.com/API");
define("LOGIN", "/login");
define("GET_PROJECTS", "/getProjects");
define("ADD_PROJECT", "/addProject");
define("ADD_TODO", "/addItem");
define("GET_OPEN_TODO", "/getUncompletedItems");
define("GET_CLOSED_TODO", "/getAllCompletedItems");
private function init()
{
// parse the credentials.txt file for the Todoist API token for the Todoist account
if(defined("AUTH_TOKEN") == false)
{
define("AUTH_TOKEN", trim(getNameValuesFromFile("credentials.txt")["td_api_key"]));
}
return AUTH_TOKEN;
}
// Validate if a project exist or not in
function validateProject($project, $createIfNew)
{
if(isset($project) == false) return null;
// does the project already exist and if not go create it?
$the_project = projectExists($project, false);
if (!isset($the_project) and $createIfNew == true)
{
// Project doesn't exist and we are asked to created it on the fly
$the_project = addProject($project);
}
// Return the project (which could be null if it didn't exist and $createIfNew was false)
return $the_project;
}
// Get the array of projects from Todoist
function getProjects()
{
$projects = null;
$projects = json_decode(file_get_contents(API_BASE . GET_PROJECTS . "?token=" . AUTH_TOKEN));
return $projects;
}
// Check if a project exists and return it
// Optionally if inboxOnNull is true return the inbox project
function projectExists($project, $inboxOnNull)
{
$projects = getProjects();
$inboxProject = null;
if(isset($projects))
{
foreach ($projects as &$test)
{
// Check for the inbox project if flag was set to true
if (($inboxOnNull == true) and ($test->inbox_project == "1")) $inboxProject = $test;
// Do we have a match with the passed in project name?
if(strcasecmp($test->name, $project) == 0) return $test;
}
}
return $inboxProject;
}
// Add a project to Todoist
function addProject($project)
{
$color = rand(1,22); // randomize the color for the new project
$new_project = json_decode(file_get_contents(API_BASE . ADD_PROJECT . "?token=" . AUTH_TOKEN . "&name=" . urlencode($project) . "&color=" . $color));
return $new_project;
}
// Get all uncompleted items for the given project
function getUncheckedItems($project)
{
$todoItems = null;
$todoItems = json_decode(file_get_contents(API_BASE . GET_OPEN_TODO . "?token=" . AUTH_TOKEN . "&project_id=" . $project->id));
return $todoItems;
}
// Get all completed items for the given project
function getCheckedItems($project)
{
$todoItems = null;
$todoItems = json_decode(file_get_contents(API_BASE . GET_CLOSED_TODO . "?token=" . AUTH_TOKEN . "&project_id=" . $project->id . "&limit=500"));
return $todoItems;
}
// Create a new todo in Todoist
function processTodo($todo)
{
// If no todo we just return
if (isset($todo) == false) return;
// We got something so lets setup the Todoist token first
init();
// Validate the project was set. Create it if non existing or ignore it when creating the todo item.
$the_project = validateProject($todo->project, true);
// Do we have any labels that we should be adding in the todo item?
$td_title = $todo->title;
if (isset($todo->labels))
{
// Todoist already has server side code to parse the @xxxx from the todo item
// So we just loop over the labels and at them at the very end of the todo title we send to Todoist
foreach ($todo->labels as &$label)
{
$td_title = $td_title . " @" . $label;
}
$td_title = trim($td_title);
}
// Now call add the todo on the API
$todo_params = "&content=" . urlencode($td_title) . "&priority=" . $todo->prio;
if(isset($the_project)) $todo_params = $todo_params . "&project_id=" . $the_project->id;
if (isset($todo->date_string)) $todo_params = $todo_params . "&date_string=" . urlencode($todo->date_string);
$new_todo = json_decode(file_get_contents(API_BASE . ADD_TODO . "?token=" . AUTH_TOKEN . $todo_params));
// Sometimes the date string can result in failure. Need to capture:
if (strcasecmp(print_r($new_todo, true), "ERROR_WRONG_DATE_SYNTAX") == 0)
{
// Ok we try again but without the date_string field. We will add a note to the item explaining what was done wrong
$note = "ERROR_WRONG_DATE_SYNTAX\r\n\r\nYou entered an date format that could not be parsed correctly by Todoist.\r\n Your date text: " . $todo->date_string;
$note = $note . "\r\n\r\nPlease take a look at the Todoist documentation and try again!\r\n\r\nhttps://todoist.com/help/datestimes";
$todo_params = "&content=" . urlencode($td_title) . "&priority=" . $todo->prio;
if(isset($the_project)) $todo_params = $todo_params . "&project_id=" . $the_project->id;
$todo_params = $todo_params . "¬e=" . urlencode($note);
$new_todo = json_decode(file_get_contents(API_BASE . ADD_TODO . "?token=" . AUTH_TOKEN . $todo_params));
}
return $new_todo;
}
// Parse text for Todo items
function parseTodos($parseInput)
{
// See the readme.md file for details on the current structure / syntax for TD items to be parsed using the below logic.
// https://github.com/s3frank/todoist-processor/blob/master/README.md
$strTest = $parseInput;
$re = "/(?:\\n|^)(?:\\s*)(?:(?:\\-|\\*) )(?:\\[( |x)\\] )(.+)/i";
$rgx_count = preg_match_all($re, $strTest, $todos);
if($rgx_count > 0)
{
// We found todo items, so lets loop over them and build an easy to use array that will be easier to process later
$todoItems = $todos[2];
$todoState = $todos[1];
$results = array();
while (list($key, $val) = each($todoItems))
{
// Create an object and set values on it by name
$todo = new stdObject();
$title = $val;
// The title field can contain in left to right order:
// priority: !=high, ?=low and nothing = normal
// title: the main title
// datestring: marked by ::
// project: marked by //
// labels: each marked by @
// Extract priority from the title which is marked with ! at the start of of the title.
if (substr($val,0,1) == "!")
{
// high
$todo->prio = "4";
$title = substr($val,1);
}
else if (substr($val,0,1) == "?")
{
// low
$todo->prio = "1";
$title = substr($val,1);
}
else
{
// normal
$todo->prio = "2";
}
// Find all the labels using a reg expression
$hasLabels = strpos($title, "@");
if($hasLabels > 0)
{
// Find all labels using a regex.
$re = "/((?<=@)[^@]*)/";
//$str = "* [ ] some stuff is here but @lab1 @lab 2 @lab3";
$str = $title;
preg_match_all($re, $str, $labels);
if (isset($labels) and is_array($labels))
{
// We have labels in an array. Loop over them an trim each item before adding it to the todo object
$clean_labels = array();
foreach ($labels[0] as &$label)
{
array_push($clean_labels, trim(preg_replace('/\s+/', '', $label)));
}
// add the labels to the todo item and the cut the title for next step
$todo->labels = array_merge($clean_labels, array());
$title = trim(substr($title, 0, $hasLabels));
}
}
// Find the project and then cut the title from the right side
$hasProject = strpos($title, "//");
if ($hasProject > 0)
{
// We found our marker so grab the data and cut the title
$project = substr($title, $hasProject+2);
$todo->project = trim($project);
$title = trim(substr($title, 0, $hasProject));
}
// Find the date string and then cut the title from the right side
$hasDueDate = strpos($title, "::");
if ($hasDueDate > 0)
{
// We found our marker so grab the data and cut the title
$date_string = substr($title, $hasDueDate+2);
$todo->date_string = trim($date_string);
$title = trim(substr($title, 0, $hasDueDate));
}
// Now we have stripped the title to what it should be so add that property in as well
$todo->title = $title;
// Was the item already checked or not?
$todo->checked = strcasecmp($todoState[$key], "x") == 0 ? true: false;
// Add the new todo object to the results array at the end
array_push($results, $todo);
}
return $results;
}
else
{
// We didn't find any Todo items so lets just return null
return null;
}
}
// Low level Helper functions for the parser to work easily.
class stdObject {
public function __construct(array $arguments = array()) {
if (!empty($arguments)) {
foreach ($arguments as $property => $argument) {
$this->{$property} = $argument;
}
}
}
public function __call($method, $arguments) {
$arguments = array_merge(array("stdObject" => $this), $arguments); // Note: method argument 0 will always referred to the main class ($this).
if (isset($this->{$method}) && is_callable($this->{$method})) {
return call_user_func_array($this->{$method}, $arguments);
} else {
throw new Exception("Fatal error: Call to undefined method stdObject::{$method}()");
}
}
}
function getNameValuesFromFile($file)
{
$data = file($file);
$returnArray = array();
foreach ($data as $line)
{
$explode = explode(":", $line);
$returnArray[$explode[0]] = $explode[1];
}
return $returnArray;
}
?>