Documentation, current as of 2024, on fsbot, an irc bot that hangs out in #emacs on libera.
The bot is called fsbot. The software is called erbot. 
Fsbot ("free software bot") is an infobot that has supported several free software channels on irc since c. 2001.


(Please don't change the design or contents of this document, except for minor cleanup. -deego).


== Parsing/Lispification, Lisp interpreter and fslisp ==

At its core, fsbot is an infobot - it allows you to add **notes** for **terms** and spits out the notes when requested. Terms are also known as **factoids**.

To set the **notes** for new **term** "myterm" to "my notes", you can type one of three things: (1) ##,myterm is my notes## (2) ##,(fs-set-term "myterm" "my notes")##, or finally, (3) ##,(set-term "myterm" "my notes")##. (1) is referrred to as a natural-language- or English- syntax. (2) is the lisp form, and finally, (3) shows that if the `fs-' prefixes are omitted, a codewalker adds them right back. Internally, all three expressions eventually convert to form (2) which then converts to ##(fsi-set-term "myterm" "my notes")## before actually getting executed. 

You often use the easiest paren-free English syntax to command the bot, but as is inevitable with any NL-parsing, you will eventually run into ambiguities, edge-cases and conflicts. Your best option in such cases is to use the precise lisp syntax for more fine-tuned control. But, you shouldn't have to remember the equivalent lisp-form. You should simply be able to look it up! You simply ask the bot itself via ##(fs-parse)## which is also aliased to ##(fs-lispify)##: 

    <deego> ,(parse "Emacs is a good editor")                                                                                                                                                  
    <fsbot> (fs-set-term "Emacs" "a good editor")


This tells you that your English sentence get parsed as a lisp form. Sentences of the form "foo is bar" (often) get parsed as the `fs-set-term' form. The parser will not always read your mind perfectly, so you are very welcome to use lisp versions directly! So far, the bot has merely parsed the command, not actually executed it. 
Next, you actually execute the command and tell the bot that Emacs is a good editor:

   <deego> ,Emacs is a good editor                                                                                                                                                          
   <fsbot> Created new note for "Emacs"

As should be clear by now, you could have also used the lisp-form to effect the above:

    <deego>  ,(set-term "Emacs" "a good editor")
    <fsbot> Created new note for "Emacs"                     


This sets the term, persistent across sessions, in the publicly available file **botbbdb**.

An additional note for an existing term is entered via "is also" whose parse is:
 
  <deego> ,(parse "Emacs is also a great OS")                                                                                                                                          
  <fsbot> (fs-set-also "Emacs" "a great OS")


As we mentioned, for convenience, you can skip the `fs-' prefix when commanding fsbot, and it is always automatically added for you. Thus, instead of having to type ##(fs-parse ..)##, you were able to simply type ##(parse...)##.

Let's assume we added 11 more notes for the term "emacs." Now, how do we read them off? Just say emacs! - 

  <deego> ,emacs
  <fsbot> emacs is [0/12] an extensible, customizable, self-documenting real-time display editor <http://www.gnu.org/software/emacs/>
  <fsbot> [1] began as a set of macros for the TECO editor in 1976 by gls and rms
  <fsbot> [2] frustratingly inadequate, but often better than any extant alternative (see ,salespitch)
  <fsbot> [3] heavily optimised to navigate its own code ;;[ ,more / ,dump]


When you just say ##,emacs##, as above, the bot considers various interpretations, as will be described later. Here, it eventually decided to describe the term emacs for you. Here are some alternatives you could have used, in order of increasing reliability and decreasing brevity: (1) ##,emacs## (2) ##,describe emacs## (3) ##,(describe "emacs")##

There's a bunch of other functionality available to manipulate terms, notes, their order, merge/cp/rm terms, etc., but let's move on to the next topic.



    <deego> ,,sbcfoo is also short for single board computer (ie. a raspberry pi)                                                                                                         
    <fsbot> D'oh!  [set-also] But there’s no such term: sbcfoo

As we saw above, the bot understands and executes a limited and modified subset of lisp we refer to as **fslisp**, meant to help you control some of the bot's behavior. Furthermore, it sandboxes everything to ensure we internally always stay in the `fs-' namespace. These `fs-' functions and macros use various codewalkers, are carefully designed to avoid the irc user being able to do things like ##(shell-command "rm -rf /")##, ##(eval '(kill-system))##, etc, and are therefore sometimes very different from their usual emacs counterparts. Another source of difference arises because they were written with bot-related functionality in mind. For example, `fs-replace-string' is used to replace strings within the notes of terms, and is thus very  different from the usual `replace-string' found in emacs.

    <deego> ,fslisp 0 0 
    <fsbot> fslisp: Meant to help you modify bot's behavior. Is NOT an elisp interpreter, nor is compatible with elisp - please use your own Emacs instead! .. + other notes



*Building blocks:* You have about 1000 such `fs-' commands available, referred to as **botcommands**. Internally, some botcommands are macros and some are functions. These include some powerful building blocks like `fs-while', `fs-mapcar' and cousins, `fs-eval', `fs-read', `fs-error', `fs-setq' and `fs-makunbound', `fs-apply', `fs-funcall', `fs-quote', `fs-lambda', etc.  


Among those included are `fs-defun', `fs-fmakunbound', and `fs-defalias'. In other words, you can build upon the initial botcommands and create - or remove - as many more as you like.

Thus, you have read/write access to the `fs-' space. This rw access applies to all new functions you create, but not to the initial core `fs-' functionality, which has read-only access. Thus, trying ##(fmakunbound 'fmakunbound)## - equivalently  ##(fs-fmakunbound 'fs-fmakunbound)## - will lead to an error because `fs-fmakunbound' is part of the core functionality. Such core functions should behave in a consistent fashion, for everyone to use, and should not change just because some irc user changed them via a PM. Another example of a **core botcommand** is `fs-set-term' - a function we discussed previously.


You can query and search among the ~ 1000 bot-commands available to you using `fs-commands':


    <deego> , (commands "^a")
    <fsbot> 7 matches.  (a ac and append apply apropos av)

This means that you have access to the following bot commands that start with the letter a: `fs-a', `fs-ac', `fs-and', `fs-append', etc.

This was, in essence, an apropos-search, but restricted to items with the prefix `fs-'. A related command is fs-apropos. It searches over all emacs functions/variables, no longer restricting to the `fs-' prefix:

    <deego> , (apropos "^ac")
    <fsbot> 11 matches.  (acapulco accept-process-output access-file
        accessible-keymaps ache aching acid acons acos
        activate-input-method active-minibuffer-window)


Since you have access to `fs-defun' and `fs-setq', you can define your own `fs-' functions and set your own `fs-' variables. Once defined, these remain persistent across sessions. These are referred to as **userfunctions** and **uservariables**, respectively, and get stored in the publicly available files **userfunctions.el** and **uservariables.el**. /Uservariables should not be confused with terms/, which are set via `fs-set-term', as illustrated earlier. 



=== Context-dependent parse of NL syntax ==


While the lisp parser is pretty stable and reliable, the English parser is anything but. The latter is heavily context-dependent. For example, 
##,describe foo## gets parsed as ##,([fs-]describe "foo")##  but ##,foo is bar## gets parsed as ##,(set-term 'foo 'bar)##. Furthermore, the latter would get parsed as ##(foo 'is 'bar)## if a user-function foo existed. The English parser also evolves with time, at the whim of the author, to try to make fsbot more DWIM-my. This context-dependence is regarded by the author as a feature, not a bug. If that is a problem, please try using the lisp syntax instead. English sucks as a programming language. That's why you also have lisp at your disposal.

Here's an incomplete list of rules and factoids related to the NL parser. The list below is mostly in chronological order. That is, the parser essentially goes down (a much longer version of) this list, till something matches.

* As you have already seen, a leading comma is equivalent to addressing and invoking fsbot. Thus, these two are equivalent: ##fsbot: tell me about Emacs## and ##, tell me about Emacs##.

* When it makes sense (that is, when a function fs-foo exists), the NL parser tries to parse ##, foo bar baz.. ## as a funcall:  ## (foo BAR BAZ..)##, where BAR and BAZ are the results of (read) on bar and baz, respectively. This one rule automatically leads to a lot of usage commonly seen: the invocation of ##,more##, ##,gpt## , ##,google##, ##,describe##, ##,find-function##, ##,flame##, ##,rr##, ##,kick##, etc. This rule, for example, is what leads to ##<deego> fsbot: + 1 2 3## resulting in ##<fsbot> 6 ..(integer)##. This rule is what leads to fsbot kicking the intended party when you type ##,kick me##. This is why ##,commands ^a## dtrt and lists all botcommands starting with a. This is why you often see emacsers type ##,require gnus## instead of the more verbose ##,(require 'gnus)##, or the even more verbose ##,(fs-require 'gnus)##. They do that, of course, to load gnus into fsbot, so that fsbot is aware of gnus-related variables, and they can then do things like ##,dv gnus-agent-foo##. Again, the latter itself worked only because the present rule parsed it as ##,(dv 'gnus-agent-foo)##.

* As already seen, ## foo is [also] bar baz... ## is parsed as adding notes ##bar baz..## to the term ##foo##. That is, sentences of these forms are converted to ##(set-term..)## or ##(set-also..)##

* Several other special English forms are supported in a similar vein. Example: ##<deego> ,tell rk1 about search emacs## is seen by fsbot as ##(tell-to "rk1" (search "emacs"))##. Notice how in this case, the parser invoked itself during the conversion. In particular, we now understand why the most prevalent usage, namely, ##,tell rk1 about emacs##  works. Combining the various rules described here (including a rule mentioned below), we see that it is equivalent to ##,tell rk1 about describe emacs##, which in turn is equivalent to ## (tell-to "rk1" (describe "emacs")##. 

* When nothing else matches, ##,foo## takes a route that's roughly equivalent to ##,(describe "foo")##. That is, it tries to look up the notes for a term foo, and then return those notes in a nicely itemized list. This is why when you typed ##,emacs## above, it spit out the 12 notes for the term Emacs. If the term ##foo## did not exist, that command would search for ##*foo*## among all terms. Several niceties are built in within that command. Example: ##,bar-baz foo## will search for, among other things, bar.*baz.*flu.  If fsbot doesn't find a matching term, then, it also searches ALL notes of ALL its terms for the provided regexp. That is, it invokes the equivalent of ##(search ..)## and, as a fallback, that of ##(search-wide ..)##. A bunch of fallbacks of increasing complexity exist as it tries to return something useful and dwimmy. As a corollary of this parser rule, you can simply type the regex itself. That is, if you wanted to search for all terms (or all notes) matching the regexp "^is.*editor", you could have simply said ##,is.*editor## instead of the more verbose ##,(search-wide "^is.*editor")##.


* To invoke fsbot mid-sentence, use a double-comma. Example:


    <rk1> How do I customize my Emacs starting behavior? 
    <msp> You should read up on ,,init


Here msp is replying to rk1, while also simultaneously commanding fsbot to output the notes for the term ##init##. As far as fsbot is concerned, msp has typed ##fsbot: (describe "init")##. Therefore, following msp's reply to rk1, fsbot spits out its notes for ##,init##.

* A second double-comma ends the fsbot-invocation. Example, msp might have responded to rk1 by saying:

    <msp> Why don't you read up a bit on ,,init,, and get back to us?

Again, fsbot sees this as simply ##,(describe "init")##, and follows msp's reply with the description of ##init##.


* In some channels such as #emacs, an automatic invocation of fsbot can happen under certain conditions even when it's not explicitly requested. If someone says ##timtowdi?## or variants such as ##What is timtowdi?##, notice that they haven't invoked fsbot. Yet, if a term timtowdi exists, fsbot will chime in anyway. It will parse this as ##fsbot: (describe "timtowdi")## and reply: ##There Is More Than One Way To Do It##.

* In such channels, any sentence ending in ##??## again invokes fsbot, and is parsed as ##,(m8b)##, which invokes magic 8-ball. 

Now, as an example, let's briefly revisit the term ##emacs## - a term we created above, and whose notes we'd wanted to spit out. We stated that ##,emacs## was parsed as (a minor variant of) ##,describe emacs##
What if another irc user had separately defined a function - ##, (defun emacs () "42")##
In that case, the defun would have taken precedence, and ##,emacs## would have parsed as ##,(fs-emacs)##, returning ##42##, instead of the 12 notes you had created. In that case, how do you spit out the notes of the term ##emacs##? In such cases, you use more verbose input to disambiguate - you type  ##,describe emacs##, or ##,(describe "emacs")##. And, while you are at it, why not compare notes with the other user, clean up the bot, and do a  ##(fmakunbound 'emacs)##.
Note that ##,describe emacs## works because of the very same parsing route. Namely, the parser, noticing that a function ##'fs-describe##  exists, decides to parse the input as ##,(fs-describe 'emacs)##. Now, that function normally expects a string, so it promotes 'emacs to "emacs", and eventually spits out the notes.
Dwimmy shortcuts often work, but are unreliable. A single invocation may, behind the scenes, easily go through 6 different shortcuts and fallbacks (such as 'emacs getting promoted to "emacs", describe getting promoted to a search and then to a search-wide), etc., before silently returning something useful. If this is annoying and you desire precise routing, use the lisp-form directly - ##,(describe "emacs")##.

In the example above, the use of defun to store data - 42 - was not the best use of defun. You should use defun when you are trying to add functionality! For data, itemize it into notes, and store it in a term! 


=== `fs-' namespace ===

Here’s another reminder that everything you do is actually happening in the ‘fs-’ space. When you ##,(defun increment (myvar) (+ 1 (apply 'myfun myvar)))##, it first internally gets parsed as ##,(fs-defun fs-increment (fs-myvar) (fs-+ 1 (fs-apply 'fs-myfun fs-myvar)))##. That works because ‘fs-defun’, ‘fs-apply’, and `fs-+' are functions/macros that are provided to you in the core functionality, and because you hopefully already ‘defun’d a ‘fs-myfun’ prior to this. Notice how the parser figures out that it needs to prefix the quoted expression as well. It converts ‘myfun’ to ‘fs-myfun’. If it didn’t, users would be able to do things like (fs-apply 'shell-command "rm -rf..").

In some contexts, however, quoted expressions should NOT be prefixed with ‘fs-’. When you type ,(format "%s" 'a), you expect the answer to be “a”, and indeed, the parser correctly figures out that it should NOT, in this case, convert 'a to 'fs-a.

‘fs-parse’ is idempotent; this equality holds: (equal (parse <foo>) (parse (parse <foo>)). Thus, other than the fs-prefix codewalker, lisp-expressions stay unaltered after parsing.




=== `fs-' stripping ===
The definition of `fs-increment' that you just created is built upon several other `fs-' functions. As we alluded to above, you can immediately see your new function in the publicly available file userfunctions.el. Or you can ask fsbot to `find-function' the function for you and return a pretty-printed output. When you type ##,(find-function 'fs-increment)##, fsbot will return a readable version of the definition, by stripping all the `fs-' prefixes. Thus, when you see the bot returning this definition: ##(lambda (myvar) (+ 1 (apply 'myfun myvar)))##, keep in mind that's just a readable shorthand for ##(lambda (fs-myvar) (fs-+ 1 (fs-apply 'fs-myfun fs-myvar)))##. The former does not always equal the latter. Fsbot's output is readable but inaccurate if taken literally. If you want the bot to retain the `fs-' prefixes when printing the definition, you can use other botcommands such as `find-function-literally' or `ffl'.


=== More on fs-find-function ===
##(fs-find-function 'foo)## first looks for an emacs function `foo', and when that doesn't work, it looks for a userfunction `fs-foo'. Thus, provided a built-in `foo' does not exist, you can use `foo' to refer to `fs-foo' when using fs-find-function. `fs-ff' is an alias for `fs-find-function'. Putting all these shortcuts together: You will often see #emacsers typing the English-form ##,ff myfun## instead of the more verbose form ##,(fs-find-function 'fs-myfun)##. The English form gets parsed as ##(fs-find-function 'myfun)##, which initially looks for `myfun', falls back upon `fs-myfun', and eventually returns the `fs-' stripped definition of `fs-myfun'. You will find similar shortcuts and fallbacks in many other botcommands, meant to promote convenience and brevity. Here's another example: `df', which is a shortcut for `describe-function' has yet fallback: it punts to `fs-describe-variable' when the latter is expected to work. That arose because far too often, I noticed people trying `df' when they actually meant `dv', which is a shortcut for `fs-describe-variable'.


== GPT interface ==

**Please note that your that input that invokes chatgpt does *not* become part of openai's training corpus.**

The recommended way to invoke GPT is to use ##!## or ##,g##  when talking to the bot.
These special handlers take care of quoting the rest of your input properly, and passing it verbatim.
 
    <deego> ! which is the world's tiniest editor (((" ? 
    <fsbot> The world's tiniest editor is probably "Micro Emacs" or "µemacs", which is a compact text editor designed for Unix-like operating systems

Notice that the gibberish at the end of the text did not, in this case, cause problems. Normally, such gibberish would be passed through fsbot's read feature and lead to an error, but that feature is disabled for the above three special handlers. The read feature is otherwise useful for most other bot functionality, for example, when you simply type ##,+ 1 2##. 

I believe fsbot provided the earliest public gpt interface on the libera network. Additionally, even when not invoking GPT directly as above, fsbot used to have a way to use GPT as one of the fallback options. The results were pretty good, in my opinion, and that ensured that unmatched queries often led to a sensible response. That route, however, was disabled after user feedback, especially since this is non-free software. In future, I hope to move to using a free LLM instead of the one from openai.  

Related: Extended English Interface using ,,, which also uses GPT.

== Interaction History, logging and trash: ==

*Summary*: Use ##,history## for your private session-history. ##,dump## to dump previous interactions, ##,trash*## to see undo-information, and ##,botlog## to see everyone's rw changes. 


*Interaction:* Every interaction with the bot is logged via multiple logging mechanisms, detailed below. (The full record of) an *interaction* refers to the set {your command, its parse, the bot's response, and finally, any *undo-information* it generated}. Each interaction is assigned a random, unique name.

*Privacy:* Every interaction, especially when *rw*, enters a permanent, public record on the web. Please plan accordingly! Though *ro* interactions are somewhat semi-private to you, as explained below, I can make no guarantee of privacy even for this case. 

*ro* and *rw*: *rw* refers to an interaction that attempts to make any changes to the bot database, set any term or alter any functions or variables  (even temporarily.).  *ro* refers to other interactions that merely query a bot for information.

*Logtmp:* Every interaction generates a temporary log, available in an unlisted url under the logtmp/ folder.  These logs are subject to periodic deletion. 

*History:* When /query-ing the bot, ##,history## will list all your previous commands along with the name assigned to the corresponding interaction-record. In a channel, it lists a history of the entire channel's interactions, irrespective of the author. To dump the full record, use ##,dump##.

*Dump:* ##,dump N## will dump the n'th-oldest interaction. ##,dump <partial-name>## is another way to dump the interaction, where the name was looked up via typing ##,history##.

*Logfull:* rw interactions also get listed under the logfull/ folder. Unlike logtmp, these logs are permanent. As mentioned above, the record includes all undo-information.

*Botlog:* Also available in the botlog/ folder on the web. ##,botlog N## will dump a list of all rw interactions on day #(today - N). This includes every single rw change, made by any user, on any channel. Every interaction will also include a pointer to its logfull-record.

*Undo:* Every rw interaction's record also includes an *undo* entry: enough information to reconstruct the original database. This information becomes part of a permanent public record on the web. It also gets stored in the *trashcan*.


*Trashcan:* While all undo-information is part of a permanent public web record, it is also stored in the bot's db temporarily via a trash-mechanism. A *trash-term* is simply a term that starts with "FsTrash". Whenever any undo-information is created, new trash-term(s) are created. ##,trashlist## returns a list of trash-terms, which can then be queried like any other term. You can then use this information to undo any accidental damage. ##,trashtrash## empties the trashcan; that is, it deletes all terms starting with FsTrash.




== Extended English interface using triple-comma ==
(New, in 2023, under construction.)

You want fsbot to output its notes about the term "emacs." Or, just list its notes 3 through 6.

Or, you want to swap the term "emacs" with "vi."

Or, cp the term emacs to elisp.

Or, rename the term elisp to emacs_lisp

Or, forget the tenth note for "emacs."

Or, add a new note for "emacs."

Or, create a new term "elisp."

Or, tell <nick> about the fifth note of emacs. 

Or, move the third note of emacs to the 2nd position.

I always see people struggling, especially with that last case.  Do you always remember the English syntax for that?  ## ,3->2 in emacs ##,  or its equivalent lisp parse: ##, (rearrange 3 2 "emacs")## ?  Of course not! Well, just command the bot using whatever portion of the syntax you remember, using the shiny, new ##,,,##  If you don't remember the syntax at all, even plain English can often work:

   <deego> ,,, move the thiird note of emacs to the 5th postition                                                                                                                       
   <fsbot> Moved note 3 to 5 in "emacs"

It worked, even though I made two typos!

And, of course, once again,you can also access the extended parser itself: 

    <deego> ,(gparse "will you please merge the term emacs with elisp")
    <fsbot> (merge "emacs" "elisp")




== Google ==
To invoke google, type ##,gg## or ##,goog## or ##,google##. 
##,gge fsbot## is equivalent to ##,gg site:emacswiki.org fsbot##.
##, gg 2 fsbot emacs## will return the top two google results for ##fsbot+emacs## 

If you wish to pass on double-quoted arguments to google, it's best to use the lisp syntax.
Example:

    <deego> ,(google 1 "\"richard stallman is just my mundane name\"")
    <fsbot> https://www.cs.mcgill.ca/~rwest/wikispeedia/wpcd/wp/r/Richard_Stallman.htm
    <deego> ,(google 1 "\"richard stallman is my just mundane name\"")
    <fsbot> ""  ..(string)
Notice that the second request returned no results.


== Other minor features ==

erbot has many other features developed over 20+ years of irc usage. New ones come and go. Some fall into disuse. The best way to learn them is to just start using fsbot and see how people use them on #emacs. Here's a partial list.

* redirect terms. Modifiers within terms such as noecho, directonly.
* tell foo about bar
* karma
* praise
* flame, spook, doctor, yow, fortune, fact (rms), geek-code
* A sandboxed octave evaluator, usually disabled in fsbot. It tries to make the input safe before executing it, but almost surely has some obscure security holes.
* A plurality detector.
* A tiny, live fslisp emulator, as already discussed. Not fully compatible with elisp.
* Dvorak/colemak/qwerty battle
* A dunnet player
* An interface to wtf
* ,foo is lisp (lisp-form). If the notes for the term foo are of this form, then the lisp-form is evaluated. That has its uses, though you might simply prefer ##(defun foo(...))## in some cases. 





== Sensible Fallback Design. Read- vs Write- interactions  ==
A very large percentage of time, fsbot is invoked "incorrectly," but still manages to dtrt because of this sensible fallback design philosophy that we have emphasized throughout this document. And, fsbot, programmed in 2000s, manages to do this with pre-llm tech! Here are a few examples -

* <deego> you might want to see if ,,envrc helps you.


Here, the user forgot the closing ,, after envrc. The user ended up asking fsbot to search for "envrt dtrt for you" rather than for "envrc". However, fsbot returns the term for envrc anyway! Here's another example - 

* <technomancy> kickingvegas: is "package" here a package.el thing or a ,,df featurep thing?

Here, the user accidentally asked fsbot to (describe-function 'feature 'thing), thus supplying an extraneous gibberish argument, but fsbot dtrt. Next - 

* <deego> fsbot do you like me? <fsbot> fsbot loves you!

In some cases, as above, it returns a vaguely sensible response, even though "fsbot do you like me" matches nothing in its database.

* <deego> fsbot: emacs?

There's no term "emacs?" in its db but it figures out you are looking for the notes for the term emacs.

* <deego> , EmaCs

There's no term " EmaCs". Notice the extraneous leading space, and the funny case. But again, the bot dtrt.

*
    <deego> what is vi?                                                                                                                                                                     
    <fsbot> vi: [0/6] a visual editor (see ,viper ,vim ,nvi) ;;[ ,more / ,dump]



There are numerous other cases, and at some point, I will populate some more examples above. In each case, the user doesn't notice, but fsbot tried various approaches, till it found something sensible.


=== Less forgiving during write-interactions ===

While read interactions are very forgiving, as above,  write interactions are more conservative, by design. Here's a (partially made-up) example where it tried to return something useful:

    <cluck> ,sbc

    <+fsbot> SBCL is [0] Steel Bank Common Lisp

Now, the user may not immediately notice that the bot returned notes for SBCL rather than for sbc.  Often very useful this way. And, anyway, No harm, no foul, since this was a read interaction. Now, if you continue and try to add more notes to the (non-existent) term "sbc", it could (a) create a new term, OR (b) flag an error that would help you catch your oversight. I believe the latter is the right thing to do here.

    <deego> ,sbc is also short for single board computer (i.e., a Raspberry Pi)

    <fsbot> D'oh! [set-also] But there’s no such term: sbc

There's a bit of Postel's law going on here - I believe that during write-interactions, being conservative is the right thing. This helps flag user errors before they happen. IME, much harder for users to fix the DB after they mess it up.



== Practical Hints for best results == 



=== Practical hints for users ===


* Don't try the fs- or fsi- prefix on your own. Leave them to the bot.
* When edge-cases spoil the English parse, use ##(parse "non-edgy english-command")## to see the lisp equivalent, and then use lisp instead.
* Use ##,dump## for verbose output.
* Don't confuse variables, functions, and terms. The latter are the nuggets of the knowledge-base of the infobot. Don't confuse variables and botvariables. Don't confuse emacs functions and botcommands.
* The bot is primarily meant to be an infobot, and everything is programmed with that (and irc-safety) in mind. You will run into disappointment and incorrect answers if you expect fslisp evals to always equal those of elisp.

=== Practical hints for programming your own botcommands ===

As an irc user, you can not only add to the bot's knowledge-base (terms), but also modify the bot's behavior itself. You can program your own botcommands using the included ##(defun)##. Botcommands, when not done right, can actually make the bot less useful! Now that your function exists, the parser will often direct the bot to your command, and now, you'd better return something useful instead of causing errors! Were it not for your poorly designed function, the bot could have taken another route and provided some useful output! So, how do you dtrt?



* Remember that your botcommand will often be invoked by irc users via English usage: ##,myfacility foo bar 1 2 orly? more_garbage_here##
* Try to make your facility fault-tolerant. Make sure it works whether invoked from English or from lisp. There will often be trailing garbage, or not enough args, or too many args. 
* Thus, when you want to accept N args, always use ##&rest ignore## so your botcommand will ignore trailing garbage and still output something useful. Your shiny new ##dict## should work even if user says: ##,dict combobulation might be it##. Here, clearly, ##might be it## is meant for other irc users. Your one-arg function receives multiple arguments. Instead of returning an error, you should ignore the trailing garbage. It had better work even when the arg is a symbol 'combobulation instead of a string, as will happen during the above Engilsh usage. Or, when it is a symbol, or a list, or another lisp object.
* When not enough args, here's a sample response -  ##(error "Usage: ,myfacility [num_items] reason [more_reasons]")##. This is an example of a facility that also accepts an additional numeric argument at the beginning.
* Examples of actual dwimmy usage from erbot  - ##,google 3 emacs erbot##. Here, 3 is an optional numeric argument. It runs google on the remaining terms, but outputs only 3 results. User can supply double-quotes to google via, say,  ##, ( google "\"hi there\"" "emacs" "site:emacswiki.org") ##
* Another example of dwimmy usage: ##,ops eric spamming too much##.  IIRC, this interprets the first item as nick, and the remaining phrase as one single arg - the reason for the complaint to ops. It also knows to direct the output to the ops for the particular channel in which it is invoked.
* `stringifyd' almost always dtrt to interpret user-args in a dwimmy fashion from both English and lisp. For many usecases, your facility should often look like - ##(defun myfacility (&rest args) (setq args (stringifyd args)) (now do the actual work) )##.
* This will handle many edge cases, whether called from lisp or English. It will effectively convert ##(myfacility "aa bb" "cc" "1" 2)## to ##(myfacility "aa" "bb" "cc" 1 2)##. It should also parse ##,myfacility aa bb cc 1 2##  correctly and do the same. Notice how, among things, it also broke up "aa bb" into two args. 
* Remember that English parses ##(read)## each arg in turn before sending it to your botcommand. So, you might receive symbols when you were actually expecting strings. Also notice that in some use-cases, myfacility may  receive  "2" after an English parse when the user actually meant the integer 2. 
* Your facility may be invoked by the user via English, via lisp, or via other facilities that build upon your function. Or, the user might invoke several nested facilities within the same lisp command. Your botcommand might receive input args as integers, as strings, as symbols, etc. It might even receive just one string comprising the actual args separated by spaces. Thus, a rough "universal" flowchart, (unless you use the above tricks such as ##stringifyd##), is to ##(format "%s")## each arg. And, after that, if its ##(read)## is an integer, interpret it as one. Or, if you are expecting a symbol, you go ahead and (read) to convert it to one.

=== Practical hints for developers ===
This section applies if you have been given edit rights to the erbot software itself. Here's a partial list - 

* Your primary concern is to not introduce exploits.
* It is best to leave any changes to the core sandbox, parsing, functionality to deego. Because: (1) exploits; (2) In any case, it is a minefield, right?  You may think you are facilitating a new command-interpretation route, but you probably broke 50 other use cases developed over 20+ years.
* Thus, most of your (and my!) time will be spend introducing new  ##fsi-## (or ##fs-##) functions or editing existing ones. But, even here, you can very easily introduce exploits. Think of the MANY ways your function can end up getting invoked. The arg you are parsing could be a string, number, quoted item, an arbitrary lisp expression, or could have come to you from the eval of one. Your function's output is intended for irc output, but could also be an input to another function or to a botcommand. The best way to avoid exploits is to simply use OTHER fs- or fsi- functions within your shiny, new facility. That way, assuming there were no prior exploits, you can be sure you haven't introduced one. In that case, you are like any other irc user using ##(defun)##, except that your new function is immutable - an ##fsi-##.
* See the subsection ##Practical hints for programming your own botcommmands##. 
* A secondary concern is to ensure the bot doesn't go hang for an hour in response to a user-input. Ensure you use enough ##(sit-for 0.001)## in your code when executing loops. That will allow the core to take control back via the various built-in timeouts.
 


----
CategoryCommunity InternetRelayChat EmacsChannel
