Zirak / SO-ChatBot

Other
124 stars 60 forks source link

/learnEval command #104

Open dannybeckett opened 11 years ago

dannybeckett commented 11 years ago

It would be cool if there was a learnEval command, to execute learned JS commands.

E.g.

!!learnEval divisor "var number = 31861, divisors = []; for(var i = 1; i <= number; i++) if(number % i === 0) divisors.push(i); divisors;"

When run with !!divisor, would output:

[1,151,211,31861]

It'd be even better if it could take arguments, e.g. number, so one could run !!divisor 456.

caub commented 11 years ago

How do you pass arguments to it? (ok since you edited), they could be passed the same way as learn probably, with a regex

notpushkin commented 11 years ago

May I +1? Please? Ple-e-e-ase?

notpushkin commented 11 years ago

@ci11

!!learnEval command "js_source" 
notpushkin commented 11 years ago

Ok, maybe like this then:

!!learnEval myEcho "function(msg) { return msg; }"
!!learnEvalC reverse "(msg) -> esrever.reverse msg"
caub commented 11 years ago

!!learnEval reverse "(x)=> x.split('').reverse().join('')" \w+ !!reverse racecar

racecar

notpushkin commented 11 years ago

No need to use regexps, however might be quite useful sometimes. And what should we do with large codeblocks? Maybe just indent them?

!!learnEvalC reverse
    (str) =>
      doABarrelRoll ()
      youtube.getVideo("oHg5SJYRHA0")
      str.split('').reverse().join('')

Offtop: x.split('').reverse().join('') is bad.

FirstWhack commented 11 years ago

I said this once. I still think it'd be pretty cool.

On Oct 19, 2013, at 12:48 PM, dannybeckett notifications@github.com wrote:

It would be cool if there was a learnEval command, to execute learned JS commands.

E.g.

!!learnEval dannydivisor "var number = 31861, divisors = []; for(var i = 1; i <= number; i++) if(number % i === 0) divisors.push(i); divisors;" Would output:

[1,151,211,31861] — Reply to this email directly or view it on GitHub.

mainerror commented 11 years ago

Yes, I like the idea and I think that we'll do it just the way @ci11 proposed since it is the same way we handle this in the learn command.

benjamingr commented 11 years ago

What about just accepting a function and using its arguments?

Shmiddty commented 11 years ago

How would this incorporate response patterns? <> | <msg> | <user>

benjamingr commented 11 years ago

If an argument is named user or msg then the appropriate value is passed.

!!learnEval function sum(a,b){ return a+b;}

Or without the function keyword

!!learnEval sum(a,b){ return a+b;}

Execution:

!!sum 5 10
> 15
Shmiddty commented 11 years ago

Something like func.apply({}, input.match(/\w+/g))?

mainerror commented 11 years ago

@benjamingr I don't like that at all to be honest.

  1. It introduces yet another way to define stuff.
  2. What if I want a an eval like this: function a() {}: function b() {}; ...?

I think having like as close to the command definition as possible would be the best way.

!!learnEval "name" "<>" "actual code" "argument pattern"

I split up the message type pattern on purpose, maybe it is worth thinking about splitting them out even in the normal command definitions.

benjamingr commented 11 years ago

@mainerror seriously? You'd rather have regex patterns and strange arguments over a straight up JS function?

A function is the basic JavaScript construct containing code that's aimed at executing a command. The language has blessed us with such a simple construct. Why not use it?

FirstWhack commented 11 years ago

I would say that all arguments ( | etc...) should be passed to the function no matter what. That way it's less confusing. Then if you need the value you have it, if not, no harm done.

On Oct 21, 2013, at 12:41 PM, Shmiddty notifications@github.com wrote:

How would this incorporate response patterns? <> | |

— Reply to this email directly or view it on GitHub.

shea-sollars commented 11 years ago

To make this safe, we would need to extend the eval functionality that is already there, and provide an object to the running code, to access some of the bot's API functionality.

Zirak commented 11 years ago

Accepting a function sounds silly, why not just do what commands already do (which is pretty much what shell does): Accept $0, $1 and so forth?

/learnEval foobar 'You know, $0 smells a lot like foo bar' 'Tells you the truth about things which smell like foobar'
/foobar yo mama

And pass the result of parsing it according to the bot's innate argument parsing (the spaces/quotes things).

Since it's pure code, you can run a regexp if you want, or do any other wacky transformations. This way, we can also check if certain input patterns arise (a certain convention to accepting arguments), and consider adopting it into core.

As for output, it'd be a bit tricky. bot.eval isn't too flexible, since it opts as both the command and the code runner. So we'd need to separate one of these parts out, and have the "purer" part (which just runs code) accept both code, and some information object which'll be passed along into the sandbox. On that, we can define a function or a flag or whatever which triggers how output is handled - as a "pure" message, or as a user/message reply.

Shmiddty commented 11 years ago

@Zirak let's take fuckable as an example. If you have to pass the code to the command every time you call it, it defeats the purpose.

/learnEval fuckable 'You can safely fuck anyone older than $0' ... /fuckable 28/2+7

Not much gained there.

mainerror commented 11 years ago

What are we event talking about here? It seems to me that we're deriving from topic. This is not meant to be yet another console like print command but a way to have an evaluation saved.

Zirak commented 11 years ago

@Shmiddty Of course the user doesn't have to supply the code...the user input is never part of the code, it's data passed to the code, just a variable.

/learnEval fuckable "You can shag down to $0/2+7"

Or, more realistically:

"You can shag down to Number($0)/2+7"
shea-sollars commented 11 years ago

Why the need for a whole new command? Why not just improve and extend the learn command we have now?

One concern is, how to know when we're dealing with JS? We could use parenthesis: /learn fuckable "No younger than (Number($0)/2+7)"

Result: No younger than ##

So, how would (should?) it handle a case with variables? /learn fuckable "(var safeAge = Number($0)/2+7)No younger than (safeAge)"

The problem now would be, the result being: ##Not younger than ##

But is all of that really better than this? /learn fuckable {var safeAge = Number($0)/2+7; return "No younger than"+safeAge;}

Another variation, to save a little space, can be: /learn fuckable {safeAge = Number($0)/2+7} "No younger than $safeAge" Here, the optional setup block, fully capable of JS, provides some a extended parse variables to be included in the resulting block.

Imo, we just need to provide an enclosure, and since we're letting the parser take care of the arguments, there's no need for "function()". Using brackets instead of quotes can tell the parser when we're dealing with an extensive JS command, rather than a simple print command.

Or we could get the best or both worlds. When parsing a simple print command inside quotes, we could still recognize code between parenthesis, and when parsing and extensive command between brackets, the whole thing can be interpreted as one big block of code between parenthesis.

caub commented 11 years ago

we could also have a global object for that, and access it from the current eval, it's probably more handy to define them in a js environment directly

!!> $.fuckable = (age) => age < 29;
!!> $.fuckable(27)

and have !!fuckable \w+ automatically activated then, (the learned commands would prevail over the $. methods) !!fuckable 28.9

Zirak commented 11 years ago

@ci11 That creates a bit of weird dynamics and extra magic.

@shea-sollars That doesn't simplify things, it complicates them. It means all learned commands are now null and void, that /learn now has to use elaborate parsing and composing techniques, and completely changes /learn's dynamics. I'd rather have a relatively simpler /learn which does much more basic copy-replace-paste operations and a more complex /learnEval which handles input logic than have an omnitool /learn which tries to do everything.

shea-sollars commented 11 years ago

@Zirak If I understand correctly, you mean because of how learned commands are saved, but how would this be a problem if the setup/eval block should be optional, and would always come before any other arugments? I thought it would be pretty simple to distinguish an eval argument from a print argument, based on whether or not they are wrapped in quotes versus if they are wrapped in brackets. Maybe I am misunderstanding, because there is some specific syntax that this would break?

I did a little testing, and it is very easy to get the names/values of variables set in the global scope of a worker. Probably even easier than what I came up with, if we incorporate it into the foundation of the eval worker script. The main problem I see, is that it would either need to parse the arguments twice or, more preferably, in order. Which actually shouldn't be a problem, with the eval block always coming first. In fact, maybe the eval block shouldn't even be seen or recognized as an argument at all, by commands? The eval block could be restricted to the command parse, and pass a temporary object to it for the rest of the arguments, which allows it to recognize the extra values to parse. In return, this would become a new thing for all commands, and all of the work would just need to be done in parseCommandArgs.js. /afk {var reasons=['eating','watching tv','masturbating']; reason=reasons[Math.round(Math.random()*(reasons.length-1))]} $reason

My concern with making it work in learned commands, is that it would need to parse the eval and print block each time the command is invoked.

caub commented 10 years ago

Is that feature still doable in mid-term? macros like $rand would be done in pure js, however there are still some security concern, for avoiding infinite recursion/loop that would more or less crash the bot I guess