DuckDuckGo is a general purpose search engine. We've created a platform called DuckDuckHack that enables developers to write open source plugins on top of the search engine (like add-ons for Firefox). DuckDuckGo plugins react to search queries and provide useful instant answers above traditional links.
If you want to get involved in making DuckDuckGo a better search engine, you've come to the right place!
- If you are a developer, this guide serves as a master jumping-off point for the DuckDuckHack platform.
- If you are not a developer, but would like to become one to help with DuckDuckHack, we recommend the JavaScript course at Codecademy. That will help you to get started with the Spice plugin type (explained below).
- If you have no intention of becoming a developer, there is still a lot you can do at our ideas companion site where you can suggest and comment on plugin ideas such as identifying the best sites and data sources to draw from. Similarly, you can submit issues about current plugins. Both of these activities are very valuable and will help direct community efforts.
DuckDuckHack is very much a work in progress. Some plugin types have better interfaces than others. We will be improving the platform based on your feedback. Our long-term goal is to be able to distribute all DuckDuckHack (and internal) instant answers via the DuckDuckGo API. Currently fathead and goodie plugin types automatically flow through. We are working on exposing the other plugin types, which are more complicated to distribute and can have licensing restrictions.
- For new plugins, follow @duckduckhack
- For ongoing discussion: DuckDuckHack list
We hope you will consider making DuckDuckGo plugins to:
- Improve results in areas you personally search and care about, e.g. programming documentation, gaming or entertainment.
- Increase usage of your own projects, e.g. data and APIs.
- Learn something new.
- Attribution on our site and Twitter (working on more).
- See your code live on a growing search engine!
##Overview
There are currently four types of DuckDuckGo plugins:
- Examples: reverse, private ips, dice, roman, passphrase, etc.
- Status: v1!
- Language: Perl
- Involves: processing the search query.
- Examples: xkcd, alternative to, twitter, wordnik, expatistan, etc.
- Status: v1 release candidate
- Language: JavaScript
- Involves: processing data from APIs.
- Examples: git, perl, final fantasy, emoticons, http, etc.
- Status: alpha
- Languages: Perl, Node, Ruby, Python (maybe others)
- Involves: generating information about specific queries.
- Examples: wikipedia, lyrics, stack overflow, etc.
- Status: alpha
- Languages: Perl, Node, Ruby, Python (maybe others)
- Involves: formatting data sets to answer general queries.
This repo contains all plugin-agnostic information that you'll need. In the Getting Started section below, you'll find overviews and documentation trails for Spice and Goodie plugins. These two plugin types have more well-defined processes and you just have to follow the list of links in order to learn all you need to know. For Fathead and Longtail plugins, you should see their individual repositories for how-to instructions (linked in Getting Started). The zeroclickinfo-goodies and zeroclickinfo-spice repositories each contain more detailed information on their respective plugin types and their readme's are linked to from the tracks below. At the end of a section in one of these repos, there will always be a link that will take you back to this page and the documentation trail that you have been following.
Step 1. Decide what you want to work on. If you don't have any ideas, start here.
Step 2. Figure out your plugin type (see Plugin Types above). If the right type is not obvious, please ask us. Sometimes multiple plugin types could work, and we can help you figure out which one would work best. Consider the following when choosing what type of plugin to make.
-
DuckDuckGo plugins appear at the top of search results pages, which is a sacred space! Please follow these guidelines to ensure the quickest path to going live.
-
Use the right plugin type. If your plugin uses external APIs in real time, it should be Spice. See the Xkcd integration for an example.
-
Better than links. Since instant answers are above the traditional links, they should be unambiguously better than them. For example, the Yummly integration shows recipes that are better than the links below.
-
No false positives. A false positive is an irrelevant instant answer. Only return an instant answer when you know it is good, and otherwise return nothing. For example, the Quixey plugin shouldn't show an answer for a query like "How to write an app."
-
Minimize vertical space. Only include the most important information and then offer the user to click through for more if needed.
-
Readable answers. If textual, create sentences or short statements that users can actually read.
-
Consistent design. When in doubt, copy what already exists or ask us! We already have a few cool designs.
Step 3. Fork the right repository (GitHub instructions):
- Goodies (Perl functions)
- Spice (JavaScript functions)
- Fathead (Keyword data)
- Longtail (Full-text data)
Step 4. Now it's choose-your-own-adventure time!
-
For Goodies, check out the Goodies Overview below. This will give you a list of links to other pages that will guide you through the goodie process.
-
For Spice, proceed to the Spice Overview below. This section will walk you through everything you need to build a basic spice plugin.
-
For Fathead, check out the Readme in the fathead repository.
-
For Longtail, check out the Readme in the longtail repository.
Follow this list to go through the goodie progression.
- Basic Tutorial -- this will show you the fundamentals of making a plugin. It's a simple walkthrough-by-example and gives a good introduction to the system.
- Testing Triggers -- this will lead you through how to use duckpan, our command-line utility, to test the plugins that you've written and make sure your triggers are working properly.
- Submitting Plugins -- this section guides you through the plugin submission process, and is the last section that you need to gain a basic understanding of the entire process.
- Once you're familiar with the above three sections, it's time to move on the Goodies repository, which contains information about advanced goodie creation, and the Advanced plugin-agnostic docs.
Follow this list to go through the spice progression.
- Basic Tutorial -- this will show you the fundamentals of making a plugin. It's a simple walkthrough-by-example and gives a good introduction to the system.
- Spice Handle Functions -- this section provides an overview of the different variables that a spice plugin can process.
- Testing Triggers -- this will lead you through how to use duckpan, our command-line utility, to test the plugins that you've written and make sure your triggers are working properly.
- Spice Callback Functions -- this section explains how JavaScript callback functions are generated by the plugin system.
- Testing Spice -- this section introduces you to the spice testing process.
- Submitting Plugins -- this section guides you through the plugin submission process, and is the last section that you need to gain a basic understanding of the entire process.
- Once you're familiar with the above sections, it's time to move on the Spice repository, which contains an advanced section with information about more involved spice creation, and the Advanced plugin-agnostic docs.
In this tutorial, we'll be making a Goodie plugin that checks the number of characters in a given search query. The end result will look like this and works like this. The same framework is used to trigger Spice plugins.
Let's begin. Open a text editor like gedit, notepad or emacs and type the following:
package DDG::Goodie::Chars;
# ABSTRACT: Give the number of characters (length) of the query.
Each plugin is a Perl package, so we start by declaring the package namespace. In a new plugin, you would change Chars to the name of the new plugin (written in CamelCase format).
The second line is a special comment line that gets parsed automatically to make nice documentation (by Dist::Zilla).
Next, type the following use statement to import the magic behind our plugin system.
use DDG::Goodie;
Right after the above line, you should include any Perl modules that you'll be leveraging to help generate the answer. Make sure you add those modules to the dist.ini file in this repository. If you're not using any additional modules, carry on!
Now here's where it gets interesting. Type:
triggers start => 'chars';
triggers are keywords that tell us when to make the plugin run. They are trigger words. When a particular trigger word is part of a search query, it tells DuckDuckGo to trigger the appropriate plugins.
In this case there is one trigger word: chars. Let's say someone searched "chars this is a test." chars is the first word so it would trigger our Goodie. The start keyword says, "Make sure the trigger word is at the start of the query." The system has several other keywords like start that are enumerated in the Triggers section. The => symbol is there to separate the trigger words from the keywords (for readability).
Now type in this line:
handle remainder => sub {
Once triggers are specified, we define how to handle the query. handle is another keyword, similar to triggers.
You can handle different aspects of the search query, but the most common is the remainder, which refers to the rest of the query (everything but the triggers). For example, if the query was "chars this is a test", the trigger would be chars and the remainder would be this is a test.
Now let's add a few more lines to complete the handle function.
handle remainder => sub {
return 'Chars: ' . length $_ if $_;
return;
};
This function (the part within the {} after sub) is the meat of the Goodie. It generates the instant answer that is displayed at the top of the search results page.
Whatever you are handling is passed to the function in the $_ variable ( $_ is a special default variable in Perl that is commonly used to store temporary values). For example, if you searched DuckDuckGo for "chars this is a test", the value of $_ will be "this is a test", i.e. the remainder.
Let's take a closer look at the first line of the function.
return 'Chars: ' . length $_ if $_;
The heart of the function is just this one line. The remainder is in the $_ variable as discussed. If it is not blank ( if $_ ), we return the number of chars using Perl's built-in length function.
Perl has a lot of built-in functions, as well as thousands and thousands of modules available via CPAN. You can leverage these modules when making Goodies, similar to how the Roman Goodie uses the Roman module.
If we are unable to provide a good instant answer, we simply return nothing. And that's exactly what the second line in the function does.
return;
This line is only run if $_ contained nothing, because otherwise the line before it would return something and end the function.
Now, below your function type the following line:
zci is_cached => 1;
This line is optional. We set is_cached to true (0 is false, 1 is true) because this plugin will always return the same answer for the same query. This speeds up future answers by caching them (saving previous answers).
Finally, all Perl packages that load correctly should return a true value so add a 1 on the very last line.
1;
And that's it! At this point you have a working DuckDuckHack Goodie plugin. It should look like this:
package DDG::Goodie::Chars;
# ABSTRACT: Give the number of characters (length) of the query.
use DDG::Goodie;
triggers start => 'chars';
handle remainder => sub {
return 'Chars: ' . length $_ if $_;
return;
};
zci is_cached => 1;
1;
The plugin system works like this at the highest level:
-
We break the query (search terms) into words. This process happens in the background.
-
We see if any of those words are triggers (trigger words). These are provided by each of the plugins. In the example, the trigger word is chars.
-
If a Goodie plugin is triggered, we run its handle function.
-
If the Goodie's handle function outputs an instant answer via a return statement, we pass it back to the user.
Click to return to the Goodies Overview or the Spice Overview.
There are two types of triggers, words and regex. The basic tutorial walks through a simple example of a words trigger. While you technically can use a regular expression as a trigger, we encourage you to use words triggers first, and then use a regexp to further qualify the query once the plugin has been called, like in the Xkcd example in the Spice Handle Functions section. Words triggers are several orders of magnitude faster than regexp triggers (a hash check vs. a regexp match).
start......word exists at the start of the query
end........word exists at the end of the query
startend...word is at the beginning or end of the query
any........word is anywhere in the query
You can combine several trigger statements if, for example, you want certain words to be startend but others to be start. The Average Goodie is a good example of multiple words trigger statements.
Regular expression triggers can be applied to the following query objects:
query_raw............the query in its most basic form, with no clean-up string operations.
query................uniformly whitespaced version of query_raw.
query_lc.............lowercase version of *query*.
query_nowhitespace...*query* with no whitespace.
query_clean..........*query_lc*, but with whitespace and non-alphanumeric ascii removed.
Back to Goodies Overview | Spice Overview | Basic tutorial
Before reading this section, make sure you've at least worked through the basic tutorial.
Step 1. Install our DuckDuckHack utility called duckpan:
curl http://duckpan.org/install.pl | perl
This script will setup local::lib, which is a way to install Perl modules without changing your base Perl installation. (If you already use local::lib or perlbrew, don't worry, this script will intelligently use what you already have.)
If you didn't have a local::lib before running the install script, you will need to run the script twice. It should tell you when like this:
please now re-login to your user account and run it again!
If everything works, you should see this at the end:
EVERYTHING OK! You can now go hacking! :)
Note that with local::lib now installed, you can easily install Perl modules with cpanm.
cpanm App::DuckPAN
App::DuckPAN is up to date.
Step 2. Go to your fork of the repository (a directory or folder on your computer).
cd zeroclickinfo-goodies/
Step 3. Install the repository requirements using duckpan.
duckpan installdeps
This command will install all the Perl modules used by the DuckDuckGo plugins within your local repository. These requirements are defined in the /dist.ini file (at the root).
Step 4. Add your plugin.
Make a new file in the lib/DDG/Goodie/ directory for Goodies or the lib/DDG/Spice/ directory for Spice. The name of the file is the name of the plugin followed by the extension .pm because it is a Perl package. For example, if the name of your plugin was TestPlugin, the file would be TestPlugin.pm.
Step 5. Test your trigger(s) interactively.
Type this command at the command line.
duckpan query
First, this command will output all of the plugins available in your local plugin repository.
Using the following DDG::Goodie plugins:
- DDG::Goodie::Xor (Words)
- DDG::Goodie::SigFigs (Words)
- DDG::Goodie::EmToPx (Words)
- DDG::Goodie::Length (Words)
- DDG::Goodie::ABC (Words)
- DDG::Goodie::Chars (Words)
...
You should see your plugin in there as well. When the output is finished it gives you an interactive prompt.
(Empty query for ending test)
Query:
Now you can type in any query and see what the response will be.
Query: chars this is a test
DDG::ZeroClickInfo {
Parents WWW::DuckDuckGo::ZeroClickInfo
Linear @ISA DDG::ZeroClickInfo, WWW::DuckDuckGo::ZeroClickInfo, Moo::Object
public methods (3) : is_cached, new, ttl
private methods (0)
internals: {
answer 14,
answer_type "chars",
is_cached 1
}
}
There is a lot of debugging output, but you will want to pay special attention to the internals section.
internals: {
answer 14,
answer_type "chars",
is_cached 1
}
Here you can see the answer returned, as well as any zci keywords (by default there will be a default answer_type and is_cached value).
Simply hit enter (a blank query) to exit interactive mode.
Query:
\_o< Thanks for testing!
Click to return to the Goodies Overview or the Spice Overview.
Step 1. Commit your changes.
git commit -a -m "My first plugin that does X is ready to go!"
Step 2. Get your commit history how you like it.
git rebase -i origin/master
Step 3. Push your forked repository back to GitHub.
git push
Step 4. Add your info to the plugin so we can give you credit for it on the Goodies page. You'll see your name or handle on the live site! Check out the Metadata README for detailed instructions on how to include your name and links.
Step 5. Go into GitHub and submit a pull request! That will let us know about your plugin and start the conversation about integrating it into the live search engine.
You're pretty much done! Now it's time to learn the advanced stuff! Click to return to the Goodies Overview or the Spice Overview. Each of these sections has links to follow so you can read up on the more advanced facets of plugin development.
===
Of course! Here are the easiest ways to contact someone who can help answer your questions:
- Join us on IRC at #duckduckgo on Freenode.
- Write the discussion list.
- Write us privately at open@duckduckgo.com.
If you don't know Perl, that's OK! Several plugin types are not in Perl. Also, if you know PHP, Ruby, or Python you should be able to write Goodies in Perl pretty easily using this awesome cheat sheet.
Yup! We maintain a growing list. There are also improvement ideas for Goodies, Spice, Fathead and Longtail.
In your initial pull request, please note the link on the idea list. We'll move it to the "in process" bucket for you.
Submit GitHub issues in the appropriate repo.
The ultimate arbiter is the user, and that's the perspective we take. In other words, we ask "what is best for the user experience?" That said, it often makes sense to combine ideas into one, better plugin.
Why isn't my plugin in the DuckDuckGo API?
If your plugin is spice or longtail, sometimes we can't expose it through the DDG API for licensing reasons (e.g. the WolframAlpha plugin), but our over-arching goal is to make all of our instant answers available on their own.
Maybe. There are a bunch more internal interfaces we haven't exposed yet, and we'd love to hear your ideas to influence that roadmap.
Here's what we're working on (in roughly in this order):
- better testing/file structure for spice plugins.
- better JS interface for spice plugin callback functions.
- better attribution.
- embedding plugins.
- better testing/file structure for fathead plugins.
- more defined structure for longtail plugins.
- better testing for longtail plugins.
Are there other open source projects? Yes! Check out the other repositories in our GitHub account. You can email open@duckduckgo.com if you have any questions on those.
Yes! Check out the DuckDuckGo API. Our goal is to make as many plugins as possible available through this interface. Fathead and goodie plugins are automatically syndicated through the API, and Spice and Longtail are selectively (due to licensing complications) mixed in.