-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathCode.js
344 lines (263 loc) · 10.4 KB
/
Code.js
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
/**
*
* +----------------------------------------+
* | GAS Gmail Add Parent of Nested Label |
* +----------------------------------------+
*
* rev. 2023-06-27
* rev. 2021-11-17
*
* https://github.com/CaTeNdrE/gas-gmail-add-parent-of-nested-label
*
*
* Google Action Script (GAS) that searches Gmail messages in nested
* user labels and applies their parent label if it is missing.
*
* Ensures that a Gmail labeled with:
*
* "sport/hockey" is also in "sport"
* "sport/basketball" is also in "sport"
* "sport/lax/field" is also in "sport" as well as "sport/lax"
*
* Skip Lists are used to exclude labels from this behavior.
* A label can be excluded by both lists without causing any issues.
*/
// Offsprint Skip List
// +-----------------------------------------------------------------
// | Array of labels whose offspring (children, grandchildren, etc.)
// | will be excluded from syncing. To also exclude the actual labels
// | in this list, they must also be added to the "Labels Skip List".
// +-----------------------------------------------------------------
const offspring = [
// "Sync Issues",
// "mymessages",
// "theOffspring", // eg. Non-existent Ancestor
// "INBOX" // eg. System Label
];
// Labels Skip List
// +------------------------------------------------------
// | Array of individual labels to exclude from syncing to
// | their parents, grandparents, great-grandparents, etc.
// +------------------------------------------------------
const skiplabel = [
// "SENT", // eg. System Label
// "noLabelsPlease", // eg. Non-existent label
// "notReal/andWrong" // eg. Non-existent label
];
// Log Level (loglevel)
// +---------------------------------------------
// | 1 = info [default], 2 = verbose, 3 - debug)
// +---------------------------------------------
const loglevel = 1;
/**
* +---------------------------------+
* | |
* | Be careful editing below here |
* | |
* +---------------------------------+
*/
const logs = {};
const pad = {};
const list = {};
const len = {};
// const namesList = [];
const names = { All: [], System: [], List: [] };
const sp = String.fromCharCode(160); // define a non-breaking space for use as needed.
const padDef = 3; // default number padding
const skiplog = {
Name: [], Type: [], Output: [], Count: 0
};
/**
* +------------------+
* | addParentLabel |
* +------------------+
*/
function addParentLabel() {
if (!([1, 2, 3].indexOf(loglevel) > -1)) {
loglevel = 1;
}
mylabels = Gmail.Users.Labels.list('me').labels;
mylabels.forEach(thisLabel => names.All.push(thisLabel.name));
let labelcount = names.All.length.toString();
let padnum = labelcount.length;
let fetchlog = labelcount.padStart(padnum) + " GMAIL labels retrieved";
let uline = '\n' + "-".padEnd(fetchlog.length, "-");
fetchlog += uline;
if (loglevel > 2) {
let line = 0;
mylabels.forEach(thisLabel => console.log((++line).toString().padStart(padDef)
+ sp + thisLabel));
}
// System labels
list.System = mylabels.filter(thisLabel => thisLabel.type == "system");
list.System.forEach(thisLabel => names.System.push(thisLabel.name));
fetchlog += '\n' + list.System.length.toString().padStart(padnum) + " are system labels";
// User labels with no parent
len.orphan = (list.orphan = mylabels.filter(
thisLabel => (!(thisLabel.name.search("/") > 0) && thisLabel.type == "user")
)).length.toString();
fetchlog += '\n' + len.orphan.padStart(padnum) + " have no parent";
// In a skip list and match a valid label
const relevant = mylabels.filter(
thisLabel => thisLabel.name.search("/") > 0
&& thisLabel.type == "user"
&& matchLabel(thisLabel.name, skiplabel, offspring, padDef) > 0
);
if (relevant.length) {
fetchlog += '\n' + relevant.length.toString().padStart(padnum)
+ " match skip lists"
};
// User label has parent and not excluded by a skip list
const valid = mylabels.filter(
thisLabel => thisLabel.type == "user"
&& thisLabel.name.search("/") > 0
&& matchLabel(thisLabel.name, skiplabel, offspring, padDef, 1) == 0
);
len.Valid = valid.length.toString();
fetchlog += uline + '\n' + len.Valid.padStart(padnum)
+ " user labels to search";
Logger.log(fetchlog);
if (skiplog.Name.length) { thisPad = getPadding(skiplog.Name, 3)};
// validate skip lists
if (offspring.length) validate(offspring, "Offspring", padnum);
if (skiplabel.length) validate(skiplabel, "Labels", padnum);
// List matching user labels if loglevel > 1
if (loglevel > 1) {
for (let i = 0; i < skiplog.Name.length; i++) {
skiplog.Output += (skiplog.Name[i].padEnd(thisPad, sp) + skiplog.Type[i] + '\n');
};
let txt = "Gmail User Labels that matched a Skip List";
Logger.log(txt + '\n' + "-".padEnd(txt.length, "-") + '\n' + skiplog.Output);
};
valid.forEach(thisLabel => names.List.push(thisLabel.name));
names.List.sort();
let padname = getPadding(names.List, 1); // length of longest string plus x sps (or 1 if omitted);
checking = "Checking messages in " + len.Valid + " labels";
Logger.log(checking + (loglevel > 1 ? logLabels(names.List, checking.length, padname) : ""));
/**
* +-----------------+
* | Search Emails |
* +-----------------+
*/
for (let i = 0; i < len.Valid; i++) {
let label = valid[i].name;
// Parent label's Gmail ID (retrieve using index from name)
let thisParent = label.replace("/" + label.split("/").pop(), "");
let thisParentIndex = mylabels.findIndex(thisIndex => thisIndex.name == thisParent);
let thisParentId = mylabels[thisParentIndex].id;
let thisQuery = "\-label:" + thisParent + " label:" + label; // this label but not its parent
let msgList = Gmail.Users.Messages.list("me", { "q": thisQuery }).messages; // list of matching messages
if (msgList) {
var allUpdates = +1;
let thisLog = "";
for (let j = 0; j < msgList.length; j++) { // for each msg in list
let msgNum = +j + 1;
let msgId = msgList[j].id;
let addLabel = thisParentId;
thisLog += msgNum.toString().padStart(padDef, sp) + sp + "\"" + thisParent + "\"" + sp + "added" + sp + "to" + sp + "msgID" + sp + msgId.padEnd(17, sp) + " ";
Gmail.Users.Messages.modify(
{
'addLabelIds': [addLabel]
},
'me', msgId
);
};
let thisLogPad = + padDef + 40 + (msgList.length > 1 ? 2 : 0) + label.length + thisParent.length;
Logger.log(msgList.length.toString().padStart(padDef, sp) + sp + (msgList.length > 1 ? "messages" : "message") + sp + "with \"" + label + "\"" + sp + (msgList.length > 1 ? "were" : "was") + sp + "missing the label \"" + thisParent + "\"" + (loglevel > 1 ? '\n' + "--".padEnd(thisLogPad, "-") + '\n' + thisLog : ""));
};
};
if (!allUpdates) Logger.log("All labelled messages included the labels of their ancestors");
}
/**
* +--------------+
* | Log Labels |
* +--------------+
*/
function logLabels(arr, num, pad) {
let log = '\n' + ("-").padEnd(num, "-") + '\n';
for (let i = 0; i < arr.length; i++) {
let label = (i + 1).toString().padStart(padDef, sp) + sp +
arr[i].replaceAll(' ', sp).padEnd(pad);
log += label;
}
return log;
}
/**
* +-----------------------+
* | Skip List Validation |
* +-----------------------+
*/
function validate(list, name, pad) {
let len = list.length;
let log = len.toString().padStart(pad) + (len == 1 ? " entry" : " entries") + " in " + name + " skip list"
let nomatch = list.filter(thisLabel => names.All.indexOf(thisLabel) == -1);
let system = list.filter(thisLabel => names.System.includes(thisLabel));
let nl = nomatch.length, sl = system.length;
(nl || sl) && (log += '\n' + "-".padEnd(log.length, "-"));
if (nl) {
log += '\n' + nl.toString().padStart(pad) + (nl > 1 ? " don't" : " doesn't") + " exist:";
for (let i = 0; i < nl; i++) {
([i] < 1) && (log += ("[").padStart(pad, sp));
log += sp + nomatch[i];
(([i] == (nl - 1)) && (log += " ]")) || (log += ",");
};
};
if (sl) {
log += '\n' + sl.toString().padStart(pad) + (sl > 1 ? " are system labels" : " is a system label") + " and ignored:";
for (let i = 0; i < sl; i++) {
([i] < 1) && (log += ("[").padStart(pad, sp));
log += sp + system[i];
(([i] == (sl - 1)) && (log += " ]")) || (log += ",");
};
};
Logger.log(log);
}
/**
* +-----------------------+
* | matchLabel Function |
* +-----------------------+
*/
function matchLabel(strVal, arrVal, arrDes, padDef, log, pad) {
for (let i = 0; i < arrDes.length; i++) {
if (strVal.startsWith(arrDes[i] + "/")) {
if (log == 1) {
skiplog.Count += 1;
skiplog.Name.push(skiplog.Count.toString().padStart(padDef) + sp + strVal);
skiplog.Type.push(("Ancestor:" + sp + arrDes[i]).replaceAll(' ', sp));
}
return 1;
}
for (let i = 0; i < arrVal.length; i++) {
if (strVal == arrVal[i]) {
if (log == 1) {
skiplog.Count += 1;
skiplog.Name.push(skiplog.Count.toString().padStart(padDef) + sp + strVal.replaceAll(' ', sp));
skiplog.Type.push("Labels Skip List".replaceAll(' ', sp));
}
return 1;
}
}
}
return 0;
}
/**
* +--------------------+
* | Padding Function |
* +--------------------+
*/
// returns length of longest string in array for column padding
function getPadding(list, extra) {
if (!extra) extra = 1; // define 'extra' spaces if not set
if (!Array.isArray(list)) { // if string, convert to array
var oldList = list;
var newValue = "x";
var list = [list, newValue];
Logger.log("** Type Error: \"" + oldList + "\" was a String. Now converted to Array \"" + list + "\"");
}
if (list.length) {
var longest = list.reduce(function (a, b) // determine longest string in the array
{ return a.length > b.length ? a : b; });
};
var padding = longest.length + extra; // set padding as longest string plus 'extra' spaces
return padding;
}