Skip to content

Commit d4a6f26

Browse files
committed
ChatSetAttr 0.9 Initial commit.
1 parent 3b567f5 commit d4a6f26

File tree

3 files changed

+710
-0
lines changed

3 files changed

+710
-0
lines changed

ChatSetAttr/0.9/ChatSetAttr.js

+345
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
1+
// ChatSetAttr version 0.9
2+
// Last Updated: 2016-08-29
3+
// A script to create, modify, or delete character attributes from the chat area or macros.
4+
// If you don't like my choices for --replace, you can edit the replacers variable at your own peril to change them.
5+
6+
var chatSetAttr = chatSetAttr || (function() {
7+
'use strict';
8+
9+
const version = '0.9',
10+
feedback = true,
11+
replacers = [ ['<', '[', /</g, /\[/g],
12+
['>',']' , />/g, /\]/g],
13+
['#','|', /#/g, /\|/g],
14+
['~','-', /\~/g, /\-/g],
15+
[';','?', /\;/g, /\?/g],
16+
['`','@', /`/g, /@/g]],
17+
18+
checkInstall = function() {
19+
log(`-=> ChatSetAttr v${version} <=-`);
20+
},
21+
22+
handleError = function(who, errorMsg) {
23+
let output = "/w " + who
24+
+ " <div style=\"border: 1px solid black; background-color: #FFBABA; padding: 3px 3px;\">"
25+
+ "<h4>Error</h4>"
26+
+ "<p>"+errorMsg+"</p>"
27+
+ "</div>";
28+
sendChat(who, output);
29+
},
30+
31+
myGetAttrByName = function(charId, attrName, createMissing) {
32+
// Returns attribute object by name
33+
let attr = findObjs({type: 'attribute', characterid: charId, name: attrName}, {caseInsensitive: true})[0];
34+
if (!attr && createMissing) {
35+
attr = createObj('attribute', {characterid: charId, name: attrName});
36+
}
37+
return attr;
38+
},
39+
40+
processInlinerolls = function (msg) {
41+
// Input: msg - chat message
42+
// Output: msg.content, with all inline rolls evaluated
43+
if (_.has(msg, 'inlinerolls')) {
44+
return _.chain(msg.inlinerolls)
45+
.reduce(function(previous, current, index) {
46+
previous['$[[' + index + ']]'] = current.results.total || 0;
47+
return previous;
48+
},{})
49+
.reduce(function(previous, current, index) {
50+
return previous.replace(index, current);
51+
}, msg.content)
52+
.value();
53+
} else {
54+
return msg.content;
55+
}
56+
},
57+
58+
deleteAttributes = function (list, setting) {
59+
let attr;
60+
list.forEach(function(charid) {
61+
_.each(setting, function(attrValue,attrName) {
62+
if (attrName.match(/^repeating_/)) {
63+
attr = getRepeatingAttribute(charid,attrName,false);
64+
if (attr) {
65+
attr.remove();
66+
}
67+
} else {
68+
attr = findObjs({type: 'attribute', characterid: charid, name: attrName}, {caseInsensitive: true});
69+
attr.forEach(a => a.remove());
70+
}
71+
});
72+
});
73+
return;
74+
},
75+
76+
setAttributes = function(list, setting, createMissing) {
77+
// Input: list - array of valid character IDs
78+
// setting - object containing attribute names and desired values
79+
// Output: null. Attribute values are changed.
80+
let attr;
81+
list.forEach(function(charid) {
82+
_.each(setting, function(attrValue,attrName) {
83+
if (attrName.match(/^repeating_/)) {
84+
attr = getRepeatingAttribute(charid,attrName,createMissing);
85+
} else {
86+
attr = myGetAttrByName(charid,attrName,createMissing);
87+
}
88+
if (attr) {
89+
if (attrValue.current !== undefined) attr.set('current',attrValue.current);
90+
if (attrValue.max !== undefined) attr.set('max',attrValue.max);
91+
} else if (!createMissing) {
92+
handleError('GM','Missing attribute '+attrName+' not created for character '+getAttrByName(charid,'character_name')+'.');
93+
} else {
94+
handleError('GM','Repeating attribute '+attrName+' invalid for character '+getAttrByName(charid,'character_name')+'.');
95+
}
96+
});
97+
});
98+
return;
99+
},
100+
101+
getRepeatingAttribute = function(charId, attrName, createMissing) {
102+
let attrMatch = attrName.match(/_\$\d+?_/), attr, attrNameSplit, repSectionIds;
103+
if (attrMatch) {
104+
let rowNum = parseInt(attrMatch[0].replace('$','').replace(/_/g,''));
105+
attrNameSplit = attrName.split(attrMatch[0]);
106+
repSectionIds = _.chain(findObjs({type: 'attribute', characterid: charId}, {caseInsensitive: true}))
107+
.map(a => a.get("name").match('^' + attrNameSplit[0] + '_(-[-A-Za-z0-9]+?)_'))
108+
.compact()
109+
.map(a => a[1])
110+
.uniq()
111+
.value();
112+
if (!_.isUndefined(repSectionIds[rowNum])) {
113+
attr = myGetAttrByName(charId, attrNameSplit[0] + '_' + repSectionIds[rowNum] + '_' + attrNameSplit[1], createMissing);
114+
}
115+
} else {
116+
let idMatch = attrName.match(/_(-[-A-Za-z0-9]+?)_/)[0];
117+
if (idMatch) {
118+
let id = idMatch.replace(/_/g,'');
119+
attrNameSplit = attrName.split(idMatch);
120+
repSectionIds = _.chain(findObjs({type: 'attribute', characterid: charId}, {caseInsensitive: true}))
121+
.map(a => a.get("name").match('^' + attrNameSplit[0] + '_(-[-A-Za-z0-9]+?)_'))
122+
.compact()
123+
.map(a => a[1])
124+
.uniq()
125+
.value();
126+
if (_.contains(repSectionIds, id) ){
127+
attr = myGetAttrByName(charId, attrNameSplit[0] + '_' + id + '_' + attrNameSplit[1], createMissing);
128+
}
129+
}
130+
}
131+
return attr;
132+
},
133+
134+
parseOpts = function(content, hasValue) {
135+
// Input: content - string of the form command --opts1 --opts2 value --opts3.
136+
// values come separated by whitespace.
137+
// hasValue - array of all options which come with a value
138+
// Output: object containing key:true if key is not in hasValue. and containing
139+
// key:value otherwise
140+
let args, kv, opts = {};
141+
args = _.rest(content.split(/\s+--/));
142+
for (let k in args) {
143+
kv = args[k].split(/\s(.+)/);
144+
if (_.contains(hasValue, kv[0])) {
145+
opts[kv[0]] = kv[1];
146+
} else {
147+
opts[args[k]] = true;
148+
}
149+
}
150+
return opts;
151+
},
152+
153+
parseAttributes = function(args, replace) {
154+
// Input: args - array containing comma-separated list of strings, every one of which contains
155+
// an expression of the form key|value or key|value|maxvalue
156+
// replace - true if characters from the replacers array should be replaced
157+
// Output: Object containing key|value for all expressions.
158+
args = _.chain(args)
159+
.map(str => str.split(/\s*\|\s*/))
160+
.reject(a => a.length === 0)
161+
.map(sanitizeAttributeArray)
162+
.object()
163+
.value();
164+
if (replace) {
165+
args = _.mapObject(args, function(obj) {
166+
return _.mapObject(obj, function (str) {
167+
for (let rep in replacers) {
168+
str = str.replace(replacers[rep][2],replacers[rep][1]);
169+
}
170+
return str;
171+
});
172+
});
173+
}
174+
return args;
175+
},
176+
177+
sanitizeAttributeArray = function (arr) {
178+
if (arr.length === 1)
179+
return [arr[0],{current : ''}];
180+
if (arr.length === 2)
181+
return [arr[0],{current : arr[1].replace(/^'/,'').replace(/\'$/,'')}];
182+
if (arr.length === 3 && arr[1] === '')
183+
return [arr[0], {max : arr[2].replace(/^'/,'').replace(/\'$/,'')}];
184+
if (arr.length === 3 && arr[1] === "''")
185+
return [arr[0], {current : '', max : arr[2].replace(/^'/,'').replace(/\'$/,'')}];
186+
else if (arr.length === 3)
187+
return [arr[0], {current : arr[1].replace(/^'/,'').replace(/\'$/,''), max : arr[2].replace(/^'/,'').replace(/\'$/,'')}];
188+
if (arr.length > 3) return sanitizeAttributeArray(_.first(arr,3));
189+
},
190+
191+
checkPermissions = function (list, playerid, who) {
192+
let control, character;
193+
for (let k in list) {
194+
character = getObj("character",list[k]);
195+
if (character) {
196+
control = character.get('controlledby').split(/,/);
197+
if(!(playerIsGM(playerid) || _.contains(control,'all') || _.contains(control,playerid))) {
198+
list.splice(k,1);
199+
handleError(who, "Permission error. Name: " + character.get('name'));
200+
}
201+
} else {
202+
handleError(who, "Invalid character id " + list[k]);
203+
list.splice(k,1);
204+
}
205+
}
206+
return list;
207+
},
208+
209+
getIDsFromTokens = function (selected) {
210+
let charIDList = [], characterId, token;
211+
selected.forEach(function(a) {
212+
token = getObj('graphic', a._id);
213+
if (token) {
214+
characterId = token.get("represents");
215+
if (characterId) {
216+
charIDList.push(characterId);
217+
}
218+
}
219+
});
220+
return charIDList;
221+
},
222+
223+
getIDsFromNames = function(charNames, playerid, who) {
224+
let charIDList = _.chain(charNames.split(/\s*,\s*/))
225+
.map(function (n) {
226+
let character = findObjs({type: 'character', name: n}, {caseInsensitive: true})[0];
227+
if (character) return character.id;
228+
else return '';})
229+
.compact()
230+
.value();
231+
return checkPermissions(charIDList, playerid, who);
232+
},
233+
234+
getIDsFromList = function(charid, playerid, who) {
235+
return checkPermissions(charid.split(/\s*,\s*/), playerid, who);
236+
},
237+
238+
sendFeedback = function (who, list, setting, replace) {
239+
let charNames = list.map(id => getAttrByName(id, "character_name")).join(", ");
240+
let values = _.chain(setting).values()
241+
.map(function (o) {
242+
return _.mapObject(o,function (str) {
243+
if (replace) {
244+
for (let rep in replacers) {
245+
str = str.replace(replacers[rep][3],replacers[rep][0]);
246+
}
247+
}
248+
return str;
249+
});})
250+
.map(function (o) {
251+
if (o.max !== undefined && o.current !== undefined) return `${o.current} / ${o.max}`;
252+
if (o.max === undefined) return o.current;
253+
if (o.current === undefined) return `${o.max} (max)`;
254+
return '';})
255+
.value()
256+
.join(", ");
257+
let output = `/w ${who}` +
258+
`<div style="border: 1px solid black; background-color: #FFFFFF; padding: 3px 3px;">` +
259+
`<p>Setting ${_.keys(setting).join(", ")} to ${values} ` +
260+
`for characters ${charNames}`;
261+
if (replace) {
262+
output += ' (replacing '
263+
+ _.map(replacers, arr => arr[0]).join()
264+
+ ' by '
265+
+ _.map(replacers, arr => arr[1]).join()
266+
+ ')';
267+
}
268+
output += '.</p></div>';
269+
sendChat(who, output);
270+
},
271+
272+
sendDeleteFeedback = function (who, list, setting) {
273+
let charNames = list.map(id => getAttrByName(id, "character_name")).join(", ");
274+
let output = `/w ${who}` +
275+
'<div style="border: 1px solid black; background-color: #FFFFFF; padding: 3px 3px;">' +
276+
`<p>Deleting attributes ${_.keys(setting).join(", ")} ` +
277+
`for characters ${charNames}` +
278+
'.</p></div>';
279+
sendChat(who, output);
280+
},
281+
282+
handleInput = function(msg) {
283+
if (msg.type === "api" && msg.content.match(/^!(set|del)attr\b/)) {
284+
// Parsing
285+
let charIDList;
286+
const hasValue = ['charid','name'],
287+
optsArray = ['all','allgm','charid','name','silent','sel','replace', 'nocreate'],
288+
opts = parseOpts(processInlinerolls(msg), hasValue),
289+
setting = parseAttributes(_.chain(opts).omit(optsArray).keys().value(),opts.replace);
290+
if (_.isEmpty(setting)) {
291+
handleError(msg.who, "No attributes supplied.");
292+
return;
293+
}
294+
295+
// Get list of character IDs
296+
if (opts.all && playerIsGM(msg.playerid)) {
297+
charIDList = _.map(findObjs({_type: 'character'}), c => c.id);
298+
} else if (opts.allgm && playerIsGM(msg.playerid)) {
299+
charIDList = _.chain(findObjs({_type: 'character'}))
300+
.filter(c => c.get('controlledby') === '')
301+
.map(c => c.id)
302+
.value();
303+
} else if (opts.charid) {
304+
charIDList = getIDsFromList(opts.charid, msg.playerid, msg.who);
305+
} else if (opts.name) {
306+
charIDList = getIDsFromNames(opts.name, msg.playerid, msg.who);
307+
} else if (opts.sel && msg.selected) {
308+
charIDList = getIDsFromTokens(msg.selected);
309+
} else {
310+
handleError(msg.who,"Don't know what to do.");
311+
return;
312+
}
313+
314+
// Set attributes
315+
if (msg.content.match(/^!delattr\b/)) {
316+
deleteAttributes(charIDList, setting);
317+
if (feedback && !opts.silent && !_.isEmpty(charIDList)) {
318+
sendDeleteFeedback(msg.who, charIDList, setting);
319+
}
320+
} else {
321+
setAttributes(charIDList, setting, !opts.nocreate);
322+
if (feedback && !opts.silent && !_.isEmpty(charIDList)) {
323+
sendFeedback(msg.who, charIDList, setting, opts.replace);
324+
}
325+
}
326+
}
327+
return;
328+
},
329+
330+
registerEventHandlers = function() {
331+
on('chat:message', handleInput);
332+
};
333+
334+
return {
335+
CheckInstall: checkInstall,
336+
RegisterEventHandlers: registerEventHandlers
337+
};
338+
}());
339+
340+
on('ready',function() {
341+
'use strict';
342+
343+
chatSetAttr.CheckInstall();
344+
chatSetAttr.RegisterEventHandlers();
345+
});

0 commit comments

Comments
 (0)