Sidekick Tutorial
Comments48this wiki
This tutorial will show you how to create your own Sidekick script.
When WM runs, it collects posts and labels them based first on the post's app id.
To find a post's app ID, there are many things you can do:
- Examine the via <game name here> link using either FireBug or Google Chrome and look for an appID parameter or something similar
- Examine an image location href from the game and check for an appID parameter
- Search Google for an appID related to the game. I guarantee you somebody already made this information public.
You should be an avid player and know where to get all kinds of info about a game, especially new content. Wiki sites are a great place to search. Some games even have breaking news sites made by fans, such as FrontierVille Legend.
Create a list of all the bonus types, and even go into depth of how many of each bonus item can be obtained from that bonus, especially if higher numbers make a substantial difference in the game. Here's an example list:
- 500 coins
- 250 coins
- 100 coins
- 50 coins
- 100 xp
- 50 xp
- 25 xp
- 10 xp
- Fire
- Wood
- Send Nails
- Send Wood
The WM script has many built in search methods, but here are the main 4:
- Body Text: Searches the text from the post title all the way to the comments section
- Link Text: Searches just the text in the link to get a bonus
- Link URL: Searches the url inside the link for specific code
- Body HTML Code: Searches the html behind the post from the title to the comments section
You can make use of all these function to identify a post. Below are examples pulled from various games:
- In FrontierVille, the body text "is blowin' up the woods" to date always provides a "Powder Keg" item.
- In Ravenwood Fair, a link url containing "quest_rosepetals" always sends a "Rose Petals" item.
- In Empire and Allies, most link url's containing "frType=Q" followed by anything provide "25 coins".
- In CityVille, any link text that says "go play cityville" normally takes us to the game without a bonus, so we call that an "exclude" link (more about the "exclude" magic word later)
- In Ravenwood Fair, to date, any post that contains an image "remindFriend.jpg" is also not a bonus link and needs to be excluded. We search for that using the body html.
You just need to find as many of these matches as possible. Good complex games will have huge lists that are ever expanding. Some simple puzzle games may have 10 or fewer bonus types.
Be absolutely sure your definition is sound. It should not contain a single word that might represent multiple types of bonuses. For instance, we do not check FrontierVille posts simply for the word "wood" and expect wood every time. The word wood could get wood, send wood, give a wooden post, or be some other reference altogether.
If at all possible, always use 5 letter words or greater. Four letter words or smaller are often a substring of a larger word. For instance, we never search for the word "hat" to define a post. The words: what, that, chat, etc. will always get captured as a "hat" if they come before another search term you have defined.
Be very specific, even if that means a whole sentence to identify each bonus given.
Luckily, many games are now using post defining text right in the link url. For games such as Ravenwood, Ravensky and CityVille, we can simply look for a certain parameter in the link url and that code will always exist no matter what language the poster uses. Via this method, we can bridge the language barrier without much effort. But don't expect every app to be so easy to bridge.- The URL location or locations of bonus acceptance pages
- Acceptance message text
- Failure message text
- Common errors and their text if any
So let's get started on these shall we?
- http://*.frontier.zynga.com/reward.php?*
The URL has been truncated so it only includes the part we care about. Asterisks are added where data is not useful. In this example, reward.php is the document that processes your request for a bonus or to send something, and also displays the acceptance page to your browser. Anything after that URL section usually pertains to the bonus, and we already know most everything we need to know about the bonus at this point.
Some games such as CityVille and CafeWorld have multiple pages, each designed to provide or send a different kind of item or bonus. You need ALL the URL's for these pages if your sidekick is to work properly.
To determine these page locations, you cannot just read the URL in the address bar. Many games now use iframe objects to display messages you will want to read with your sidekick. I highly suggest getting the extension called FireBug for FireFox. This extension allows you to see the tree structure that is the document DOM. If you don't understand DOM, go read up on it using Google or W3Schools. It will be important for you to know, especially if the game you create a sidekick for changes iframe locations frequently.
Now, assuming you understand DOM structure, you can now use FireBug to examine that DOM structure:
- You will want to right click on the text messages you see in the acceptance page and "inspect element".
- A panel will come up from the bottom of the screen and have sections of its own. The section you will want to use is on the lower right and has tabs, such as STYLE, COMPUTED, LAYOUT and DOM. Click DOM.
- In the DOM sub-panel you will see a treeview list of the element's properties. These properties are in alphabetical order (generally speaking, as functions appear first in green text).
- The most useful attribute I can suggest is the baseURI. Alternately you could traverse the tree structure and pull up the ownerDocument.location.href attribute.
- Copy the URL in that attribute and paste it somewhere useful, or just write it down.
Here's an example of acceptance text from FrontierVille:
- "All Outta Rewards"
A word of caution: This text is most often printed directly by the acceptance page URL and the sidekick can easily return it to the main WM script. However, some games like Ravenwood Fair, print their failure messages from another address, inside an iframe with a different domain (see previous section).
Collect ALL of the texts you can find and match them up with the following return codes:
- 2:"Responseless Collection", normally handled inside WM host
- 1:"Accepted"
- -1:"Failed", use for reason unknown
- -2:"None Left", use for reason of already taken
- -3:"Over Limit"
- -4:"Over Limit, Sent One Anyway", registers as a type of acceptance
- -5:"Server Error"
- -6:"Already Got", registers as a type of acceptance
- -7:"Server Down For Repairs"
- -8:"Problem Getting Passback Link", used only with special flags
- -9:"Final Request Returned Null Page", normally handled inside WM host
- -10:"Final Request Failure", normally handled inside WM host
- -11:"Expired"
- -12:"Not a Neighbor", neighbor-ship required to accept post
- -13:"Requirements not met", level or building requirement
- -15:"Unrecognized Response", prevent waiting for timeout if you know to watch for some known issue
GreaseMonkey takes the sidekick script as JavaScript, attaches it to the page its meant for, and then runs it as soon as that page calls an "onload" event. You don't have to do anything to start the code except provide the acceptance page urls in an @include section on top of the JavaScript file. If you are NOT new to user scripts, this is going to be easy. If not, I bet you have no idea how to write JavaScript anyway and you should be reading a JavaScript tutorial instead.
- See [www.w3schools.com W3Schools] for a really nice starter.
A sidekick script generally has 3 parts:
- A userscript header block
- A docking function
- A reading function
// ==UserScript== // @name Wall Manager Sidekick (HoN) // @description Assists Wall Manager with Heroes of Neverwinter // @include http://apps.facebook.com/neverwinterheroes/claimloot.php? // @include http://fbhonp.atari.com/client/claimloot.php?* // @include http://www.facebook.com/pages/FB-Wall-Manager/* // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/ // @version 0.0.6 // @require http://sizzlemctwizzle.com/updater.php?id=115953&days=1 // @copyright Charlie Ewing // ==/UserScript==
The above block is the user script header block. When this file is read it is automatically picked up by the script processor, in this case GreaseMonkey. The lines tell the script how to behave, and especially where to execute.
This block for HoN only includes two URLs for bonus collection. The facebook.com includes are specific statements to help it dock with the WM script. If you change or remove the facebook.com includes it may not work correctly.
You can make your sidekick script automatically update for any user by including the @require line with the url above. After you post your script to userscripts.com, your script is registered with a script ID. Change the id= parameter in that line to the id of your script, and then upload to userscripts again. Failure to change that id will cause your script to attempt to update THIS script.
For the most part, this code segment is self explanatory, but if you need more info, please use Google to figure it out. There is plenty of info out there on how to make user scripts, but this page is about how to make sidekicks for those who already understand user scripts.To properly dock with the WM host script, a sidekick needs the following data:
If left blank, the desc parameter defaults to:
- <app name nere> Sidekick (ver <version number here>)".
- Many games on Facebook use the same image proxy and you might be able to just supply "app_full_proxy.php?app=" + your appID. An empty thumbsSource parameter will now default to this value.
Setting this to true causes the main WM script to behave differently when it checks that a bonus page has loaded.
This flag should also be set if the game you are collecting for is not an apps.facebook hosted game. Non-facebook apps that post to facebook draw the user off-site and the sidekick will not be able to return information to the main script body normally.
See also the flag skipResponse for dealing with some off-facebook apps.Because the bonus collection page info may be lost if the sidekick did this clicking, we pass the info back to the WM script for processing. Setting this to true activates another part of the WM script.
Most games don't need to worry about this, and clicking their links or buttons via the script is just fine.Other games such as MouseHunt have an apps.facebook page and a non-facebook page. Some links take you off-site and will cause cross-domain errors, so forcing them to stick to their apps.facebook version is helpful.
Not many games can benefit from this option.When the status checker thinks it has found a fully loaded page in the sidekick window, the status will become "status: 2: responseless collection". All status 2 urls are stored as accepted.
- Not all load statuses are accurate, so not every page marked as accepted will be correctly accepted.
I suggest using this flag for games where the user has 100+ bonuses to collect per day and no limits. Using this flag can make bonus collection much faster because some steps are omitted. Again, this is a very inaccurate method of collecting bonuses, and every attempt should be made to try other methods.
- You cannot use skipResponse with games that also need the "requiresTwo" flag.
- You cannot use skipResponse with games that have a bonus collection page that requires another click to accept or decline a bonus type
- See the FV sidekick for a better look at alterLink use. That sidekick makes excellent use of the alterLink ability by pre-selecting 1 to 3 items from the gift page depending upon what post sent the user to that gift page.
- This is only activated when the flags object (above) contains the parameter alterLink:true.
Here's an example from the EA sidekick:
- coins10:'10 Coins',coins25:'25 Coins',coins50:'50 Coins',energy:'1 Energy'
- Test search texts are NOT case sensitive, however return values ARE.
- You cannot currently use these tests to specifically find uppercase text or the lack thereof.
- Return values must exactly match those in your accText section above, and also those in your "menu" object below.
- link: the test is run on the link text
- url: the test is run on the link url
- body: the test is run on the body text of a post, which includes the following: title, caption and desc(ription)
- title: the bold text on top of a post. Does NOT include the msg portion.
- caption: normal text below the title, not all games use this. See also desc
- desc: normal text below the title, not all games use this. See also caption
- html: the test is run on the whole post contents. In WM 2, this is not actually the html of the post, but a jumble of all the other searchable sections of the post.
- either: you can do both the link test and the body test in the same line, in that order.
- msg: you can test for text in the message portion of the post. The section above the post where users can add their own text. This is NOT included in a body search.
- img: the url for the image that goes with the post. Not all posts use this field. Mostly that is a mistake on the part of the game engine to attach one to the FB database.
- fromName: the name of the person who created the post case. sensitive as fromName
- fromID: the id of the person who created the post. case sensitive as fromID
- targetName: the array of people the post targets, name form.* case sensitive as targetName
- targetID: the array of people the post targets, id form.* case sensitive as targetID
- canvas: the location on facebook where the game runs, for instance farmville="onthefarm"
- likeName: the array of people who like the post, name form.* case sensitive as likeName
- likeID: the array of people who like the post, id form.* case sensitive as likeID
- comments: the text of all comments attached to the post
- commentorName: the array of people who commented on the post, name form.* case sensitive as commentorName
- commentorID: the array of people who commented on the post, id form.* case sensitive as commentorID
- Using relic methods to create a test:
- {url:"frType=Q", ret:"coins25"}
Searches the link url for "frType=Q" and returns "coins25" if found
- Using current methods to create a test:
- {search:"url", find:"frType=Q", ret:"coins25"}
Does exactly the same as above
- Using an array to create a test searching multiple post parts:
- {search:["link","body"], find:"send", ret:"send"}
Searches the post link text for "send", and if found returns "send". If not found, it then searches the post body with the same results.
- Using an array to create a test searching for multiple words:
- {search:"link", find:["lunch","breakfast","dinner"] ret:"meal"}
Searches the post link text for lunch, breakfast or dinner, and if found, returns "meal".
- Using two arrays to create a test searching for multiple words in multiple search regions:
- {search:["link","body"], find:["lunch","breakfast","dinner"] ret:"meal"}
Searches the post link text for lunch, breakfast or dinner, and if found, returns "meal", and if not found, performs the same search on the post body.
- Using a subTest array to identify multiple post types:
- {search:"link", find:"{%1}", subTests:["lunch","dinner","breakfast"], ret:"{%1}"}
Searches the post link text for breakfast, lunch, or dinner and returns the word found.
- Using a subTest array to identify a single post type:
- {search:"link", find:"{%1}", subTests:["lunch","dinner","breakfast"], ret:"meal"}
Searches the post link text for breakfast, lunch, or dinner and returns the word "meal".
- Using a subNumRange to identify multiple post types:
- {search:"body", find:"level {%1}", subNumRange:"1,20", ret:"getlevel{%1}"}
Searches the post body for the word "level" followed by any number in the range from 1 to 20 and returns "getlevel" followed by the number found. Numbers are run from highest to lowest to prevent "level 10" from being identified as "level 1".
- Using a subNumRange to identify a single post type:
- {search:"body", find:"level {%1}", subNumRange:"1,20", ret:"getlowlevel"}
Searches the post body for the word "level" followed by any number in the range from 1 to 20 and returns "getlowlevel" no matter what number is found.
- Using a registered expression to identify multiple post types:
- {search:"link", find:"(black|white) (pony|horse)", regex:true}
Searches the post link text for any combination of black or white, pony or horse, and returns whatever matches first
- Using a registered expression to identify a single post type:
- {search:"link", find:"(black|white) (pony|horse|bear|cat)", regex:true, ret:"adopt"}
Now you can say:
- search:region string, find:string
- search:region string, find:string array
- search:region array, find:string
- search:region array, find:string array
Remember arrays are searched in the order supplied. If you pass an array to your test object, be sure it is ordered in the way you want the searches to be performed.
See examples above.Remember arrays are searched in the order supplied. If you pass an array to your test object, be sure it is ordered in the way you want the searches to be performed. For instance if your array contains "pea" before "peanut", it will find "pea" in the word "peanut" before finding the word "peanut".
If you include the WM Library Script in your sidekick, you can call the optimize method on your array as you pass it to the test object, like this: {search:"body",find:"{%1}", subTests:myArray.optimize(), ret:"{%1}"}
See the WM Library Script documentation for details.- Example: {body:'needs {%1}',ret:'send{%1}',subTests:["rope","rock","nails"]}
In the example above, the test searches for body text containing "needs " + an array item from subTests. The test process tests in order of appearance, returning the first found value. The above example would test for "needs rope", then "needs rock" and finally "needs nails".
To optimize a subTest array, you must understand and implement the following:
- Arrays are read in order, thus an entry that starts with some string X must come before a string that is simply X. For instance, "popcorn" must come before "corn" or "corn" will be returned if a test string looks like this: "{%1} bushels".
- The same is true: "cornflower" must come before "corn" or "corn" will be returned if a test string looks like this: "send {%1}".
Depending on which value is found, the return value is then altered where the same {%1} appeared. The example above could return "sendrope","sendrock" or "sendnails".
Like the find string, subTest strings are not case sensitive. But since return text is sensitive, when inserting {%1} back to the return text, the search text is converted to lower case and has its spaces removed. For example, if "Energy Pack" was supplied in the subTests array above, the return value would be converted to "sendenergypack".
You can also omit the {%1} from the return value to return the same value for each subTest.
- Example: {body:'needs {%1}',ret:'sendmaterial',subTests:["rope","rock","nails"]}
You can also create and then provide a predefined array. This is handy when you need to use the same list more than once.
- var arrayExample = ["rope","rock","nails"];
- ...
- {link:'send {%1}',ret:'sendmaterial',subTests:arrayExample},
- {body:'needs {%1}',ret:'sendmaterial',subTests:arrayExample}
The examples above always return "sendmaterial" if the body text contains "needs rope", "needs rock" or "needs nails", or if the link text includes "send rope", "send rock" or "send nails".
The subTest parameter is exceptionally useful when many bonus types are awarded using the same basic text layout. Say you have 100+ bonus types and the bonus text appears like this:
- "wants to share a {%1} with you in FarmVille", where {%1} could be any bushel crop, a manure bag, or some other gift.
- Example1: {body: "level {%1} sample" ,subNumRange:"1,20", ret: "samplelow"}.
This would search for any text from "level 1" to "level 20" and return "samplelow" if found.
- Example2: {body: "level {%1} sample", subNumRange:"1,20", ret: "sample{%1}"}.
You may either use a return value, or by default, the test will return what is matched by the regex. Become familiar with RegExp before attempting this method.
You cannot supply an actual registered expression as a test because a registered expression is an object, and a sidekick cannot pass complex objects back to the WM host script. This is why you must supply it as text and then have WM convert it for you once docked.
Below are some examples:
- Example1: {body:"(turkey|chicken) (feed|poop)", regex:true}
This would search for turkey feed, turkey poop, chicken feed or chicken poop and return what was found.
- Example2: {body:"(turkey|chicken) (feed|poop)", regex:true, ret:"material"}
This would search for turkey feed, turkey poop, chicken feed or chicken poop and return "material".
- Example3: {body:"(turkey|chicken) (feed|poop)", regex:true, mods:"g"}
- exclude: Excludes the post from being processed. Note that "exclude" does not start with your game's appID after joining with the WM host script. Use exclude when you want to block a post, especially those meant to lure you to the game without a bonus being given, or for bonus types you don't know how to collect yet and you don't want the "doUnknown" magic menu option to collect on accident.
- wishlist: A type of exclude, except that posts flagged as wishlists can opt to not be hidden with excluded or other unwanted posts. This allows users to click the wishlist posts from WM and process them manually.
- send: A short word that can be picked up by the "sendall" magic menu option. This is useful if the sidekick does not need to know what its sending, or the user would simply not benefit in knowing that information.
- dynamic: This word will flag the post as being grabbed by the dynamic grabber, even though its being identified by your sidekick. Posts flagged with "dynamic" are simply processed as if an option was checked for their type. There is no options to disable or enable the collection of posts flagged as "dynamic". This is also the default return value given by any user created dynamic test that does not supply a ret parameter.
One such parameter could be the note parameter. Take special care when leaving notes for yourself in the note parameter as the WM does actually read and use that parameter when identifying posts using user-created dynamic tests. In fact, there is a tab in the dynamic grabber console for each test which shows the note field's contents.
All other parameters unrecognized by the dynamic grabber console are shown on the "Other" tab in the order they appear in the test object.For an example on how to build a menu object, simply view the source for any of the supported Sidekicks, or the Sidekick Starter Kit below.
Menu items can exist in the following types:
- Separator objects are stored as a variable and remember their open/closed state when you next enter the options menu.
- Tab objects are stored as a variable and remember their open/closed state when you next enter the options menu.
- New tabs which the user has not used before may not have a selected tab, causing the tab bar to look like it has no panels. Selecting a tab cures this anomaly.
- A CSS issue currently prevents tabs from using the newitem special parameter. A future version will remedy this issue.
A second array can be provided in the "clearfirst" parameter and when clicked, the button function will first un-highlight the provided array of elements. You may omit the options parameter to create just an unselect button.
This element also takes the title and label parameters.As an example, imagine there are 200 animal adoption links, of which you script every one to have an id prefix of "adopt_", and of which 20 are deer and you prefix them with "adopt_deer", where the rest of the id is the color or name of that deer type, such as "adopt_deermule". If you put all the animal adoption options in one optionblock, you would only be able to select that entire block, or single items at at time. But now using the "button_selectprefix" type with a prefix parameter of "adopt_deer", only the deer in that option block will be selected.
This button's function will select ALL options from the options menu for your appID that match the prefix given, even outside the option block, or section you enclosed it in. This allows for selecting of multiple elements from multiple groups of which you provided the same prefix. For instance you may have a group of elements scattered through your options menu that are all somehow related, but generally of different types. You could code them all as "silo_part", "silo_animal" or "silo_reward". Then you could make a button to select anything prefixed by "silo_".
Remember the selectall magic menu option already does something like this without actually checking options. You could duplicate this function by making a button with prefix "send". Remember also that items coded properly as a send function will be prefixed by "send". In the example above, if you created an element with id "sendsilo_part", a button with prefix "silo_" would NOT select that bonus type.If your sidekick needs more than one highlight color, you can opt to set newitem to 1, 2 or 3 instead of just setting it to true. These numbers correspond to green, dark red and royal blue. The value true corresponds to a more lime green color.
- Future versions may allow you to just supply a color value.
To make good use of this, you should always find and define any post you want to "exclude" to keep the game from loading without giving a bonus.
For instance, if the link text "go play" was not excluded, whenever somebody started playing a game, enabling "doUnknown" would click that link and just get taken to the game without a bonus.
- Note doUnknown contains an uppercase "U".
- Example: send a bucket of water looks like "sendwaterbucket".
You can provide the word "send" alone with no item name. To make use of just "send" your sidekick must return just "send" during its test phase, even if the actual item is not fully identified (See PT sidekick for examples). Just "send" is also picked up by the "sendall" magic option.
This becomes useful because even when a send-type post is not fully identified, sendall will still recognize it as a send-type post and will perform collection on that post.This part is very simple, but must be able to run in the exact window object(s) where those message will exist.
It might also need to perform some special tasks, such as clicking buttons to finalize collection of a bonus, or even collect a link that the WM host can process behind the scenes so as to prevent this script's function from being canceled out.
These messages are what WM has over all the other scripts and apps out there. WM can report back to the user this acceptance, failure, and many other codes that the user can easily understand. Based on these reports, the user can determine if a bonus should have been collected, or whether they should try clicking the bonus again.
Your script should go out of its way to get this information back to the user.
Look for texts that specifically state whether the bonus was collected or not, and for reasons why.
You then want to pass that information back to the WM host script via the window's url. You must return it via that window.top.location.url or the WM cannot see it. While the bonus is being processed, the WM is watching that address bar and waiting for your response. When no response is found, WM assumes you can't do your job for some reason and marks it as "timed out".
The WM Library comes with a function to do just that. Use the sendMessage function to pass back a code like this:
- sendMessage("status="+statusCode);
A list of codes you can return is found above under "The Harder Stuff > Acceptance and Failure Message Texts". These range from positive acceptance codes, to negative failure codes, some of which have special functions associated with them in the WM host script.
Good examples of how to find these messages can be found in any currently supported sidekick script, as well as in the sidekick starter kit script.You need to be able to find, handle and click these buttons using your sidekick. WM cannot do it for you.
The WM Library comes with a few functions to help you locate, grab and also click these buttons.
Depending on what that button does, you may need to make additional @include lines in your script. For instance, if clicking the button takes you to the actual game load page, you need to include that page's url in your script header block. Your script will then fire there too, and allow you to finally report back acceptance or failure from that location rather than the initial location your script fired at.
This is actually much easier than it sounds. See the various supported sidekick script for different examples.
Occasionally, you will encounter popups from facebook. These popups, as of the time of writing this, come from the facebook dialogs widget, and are currently part of facebook's "Frictionless" system. While the system does initially cause some friction, you should only ever have to deal with this widget once per user per app. All other posts you click to interact with this same person slip past the widget as previously approved.
To interact with the widgets, you may need to include some facebook url's. Be very specific or your script will run on every instance of these widgets, even for other apps. Also be specific in your coding to ensure that you are actually running within the bounds of your target app. Most instances of these widgets come complete with a url parameter defining the appID the widget is requested for. But don't rely on that alone. The widget also makes use of a form element and you can check the action of that form and block certain actions.
For example, many games use the frictionless widget for requesting neighbors. Most of those games use a location document of neighbors.php. You can check that the action of the form containing the target button does not point to neighbors.php. Otherwise all hell will break loose.
As an example of the mayhem lax @include statements can cause: In mid January of 2012, Farmville changed their neighbor adding page and made use of that widget. Due to similar changes in the FV sidekick, the combination of includes and the location of the facebook widget caused the FV sidekick to mass add neighbors and mass send gifts at the same time without requesting permission from the user. This attempted to open potentially hundreds of documents all at one moment. This caused huge problem, both for the FV servers, and for the user.
Remember, be specific.These things happen and have been scripted for in the main WM host. Games such as FrontierVille and CafeWorld use reward pages that require a second click to actually take part in the bonus, but by clicking those buttons or links, the script would then forget the actual acceptance message. The second instance of the script running on the end location would only know that the game loaded and would have to guess as to how it got there.
To prevent that, WM has the ability to accept not only a status code, but a link. That link must include all the parts of the form for which the button to be clicked is part of, and must be represented in the order that the form includes them.
It is up to you to develop code that grabs all that information and properly reports it to the WM, but the WM will actually send that form in for you. The form you send to the WM to process must not also require any more button clicking, as the WM only post the form and cannot be bothered with any of the return information except generic http error responses.
To pass a link like this back to the WM host, simply use the sendMessage function from the WM Library in a different way:
- sendMessage("status="+statusCode+"&link=["+yourLink+"]");
It includes code that is licensed under Creative Commons. The code therein is created by myself or by other sharing script writers, except where noted.
- I do not ask to be listed in the copyright section of your userscript.
The starter kit also includes basic instructional comments where useful. See the starter kit for more information.
For answers to all other questions, please refer to the wiki document above before asking below.
Get Starter Kit Script (now updated for use with WM 2)
These functions include everything you need to dock with the WM host script, as well as some good tools to make use of simplifying structures presented in the sidekick script.
- You are NOT required to use the WM Library. But you must send an attachment string to the WM main host that matches the method offered in the library.

Added by