merged with master

master
jonk1993 2016-05-20 23:59:21 +00:00
commit 84ad168f61
1025 changed files with 57772 additions and 18636 deletions

30
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,30 @@
###### What does your Pull Request do (check all that apply)?
Choose the most relevant items and use the following title template to name
your Pull Request.
- [ ] New Instant Answer
- [ ] Cheat Sheets: **`New {Cheat Sheet Name} Cheat Sheet`**
- [ ] Other: **`New {IA Name} Instant Answer`**
- [ ] Improvement
- [ ] Bug fix: **`{IA Name}: Fix {Issue number or one-line description}`**
- [ ] Enhancement: **`{IA Name}: {Description of Improvements}`**
- [ ] NonInstant Answer
- [ ] Other (Role, Template, Test, Documentation, etc.): **`{GoodieRole/Templates/Tests/Docs}: {Short Description}`**
###### Description of changes
Provide an overview of the changes this pull request introduces.
###### Which issues (if any) does this fix?
Fixes #NNNN - how specifically does it fix the issue?
###### People to notify (@mention interested parties)
---
Instant Answer Page: https://duck.co/ia/view/{{ID}}
[Maintainer](http://docs.duckduckhack.com/maintaining/guidelines.html): @mention

View File

@ -24,4 +24,4 @@ language: perl
perl: perl:
- 5.16 - 5.16
script: script:
- dzil smoke --release --author - prove -lr -j1 t

View File

@ -1,60 +1,59 @@
# Contribute to Goodie Instant Answers # Contributing an Instant Answer to DuckDuckGo.com
There are several options for contributing Goodie Instant Answers, explained below. ## Learn to Make New Instant Answers
You can always find the [Instant Answer Documentation here](https://duck.co/duckduckhack/ddh-intro). Also, if you have any questions at any point, feel free to ask on one of our community channels: Learn to make an Instant Answer by starting with our walkthroughs:
- [DuckDuckHack mailing list](https://www.listbox.com/subscribe/?list_id=197814) - Learn how to build a [convenient calculation tool](http://docs.duckduckhack.com/walkthroughs/calculation.html)
- [Slack](https://duckduckhack.slack.com) - For access, please send an email to quackslack@duckduckgo.com - Learn how to turn an [external API into delightful search results](http://docs.duckduckhack.com/walkthroughs/forum-lookup.html)
- Do not hesitate to email us directly at open@duckduckgo.com. - Create a [cheat sheet for a favorite topic](http://docs.duckduckhack.com/walkthroughs/programming-syntax.html)
## New? Make Your First Contribution Today *UPDATE: We're aiming to have multiple Instant Answers for every programming language and framework. Jump in and help us [fill in the blanks](https://github.com/duckduckgo/duckduckgo/wiki/Programming-IA-Coverage).*
If this is your first time contributing to [DuckDuckHack](http://www.duckduckhack.com), you have two great ways to quickly make your first commit: You can find the full [DuckDuckHack documentation here](http://docs.duckduckhack.com).
- **Make a [Cheat Sheet](https://duck.co/duckduckhack/goodie_cheat_sheets)** ## Improve a Live Instant Answer
Cheat sheets are a super-easy way to contribute to the live DuckDuckGo AnswerBar very quickly, by editing a single file. Cheat sheets can be about anything, from Emacs and Vim to Game of Thrones house names or wine pairings. We welcome new contributors to dive in and improve live Instant Answers. It's a great, hands-on way to learn how things work:
- **Create a simple, complete "Hello World" Goodie with our [Quick Start Tutorial](https://duck.co/duckduckhack/goodie_quickstart)** - Have a [favorite Instant Answer](http://duck.co/ia) that you want to make even better? Feel free to dive in and make changes.
- Help other DuckDuckHackers with their [Instant Answers in development](https://duck.co/ia/dev/pipeline)
- Suggest improvements & file bugs for [live Instant Answers](https://duck.co/ia) through the, "Create Issue" button on each IA page
- Tackle some of the "low-hanging fruit" and, "high priority" issues on our [Live Issues Page](https://duck.co/ia/dev/issues?tag=lowhangingfruit)
This short tutorial will lead you through all the parts of building a full-loop Goodie. This is a perfect place to start if you have an idea for an original Instant Answer. ## Go Live on DuckDuckGo.com
## Create a New Instant Answer The final step is to have your contribution appear on DuckDuckGo.com above ads and organic links, on [millions of searches](https://duckduckgo.com/traffic.html).
Once you're comfortable with the workflow and how Goodies work, we're excited to have you create your own original Instant Answer. 1. **[Create an Instant Answer Page](https://duck.co/ia/new_ia) as the home base for your idea.** If you're fixing an existing Instant Answer, [locate its existing page](https://duck.co/ia).
**1. Choose an idea** This page will be the center for feedback, Github issues, attribution, deployment, and collaboration.
Bring your own idea, or check out the ideas forum - especially [top voted answer ideas](https://duck.co/ideas/status/3?table_lnKRpLENwO2NUmZUyukQpw_sort=votes). 2. **Make sure your contribution meets the [Production Guidelines](http://docs.duckduckhack.com/submitting/checklist.html).**
**2. Plan your implementation** 3. **[Submit a pull request](http://docs.duckduckhack.com/submitting/pull-request.html).**
The first step is to research and plan your Instant Answer. Consider [the best way to implement](https://duck.co/duckduckhack/determine_your_instant_answer_type) your idea, and review the [docs and guidelines](https://duck.co/duckduckhack/ddh-intro) that apply. We're excited to meet you and support you along the way - and it's never too early to say hello. Join us on [Slack](https://quackslack.herokuapp.com) or [email](mailto:open@duckduckgo.com).
**3. Involve us** ## Formatting Your Pull Request
Before you start coding, [let us know your plans](mailto:open@duckduckgo.com). By involving us early we can provide guidance and potentially save you a lot of time and effort. Email us at [open@duckduckgo.com](mailto:open@duckduckgo.com) with what idea you're working on and how you're thinking of going about it. When submitting a pull request, the following guidelines help speed up the review process:
In addition, we'll promptly set up a [central Instant Answer page](http://www.duck.co/ia) on the community platform so others can know you're working on it and how they can help you. ### New Instant Answers
## Improve an Existing Instant Answer 1. New IAs should be titled as follows: **`New {IA TOPIC} {IA TYPE}`**. For example, `New Instagram Spice` or `New Firefox Cheat Sheet`
Another great way to contribute is to improve an existing, live Instant Answer. It's a great way to get further acquainted with Instant Answers, as well as get implementation ideas. (Many contributors report completing their first fix within two hours of forking the repository!) 2. Paste the relevant [Instant Answer Page URL](https://duck.co/ia/new_ia) in the description field. This will automatically link the PR to the Instant Answer.
**1. Choose a "low-hanging fruit"** ### Improvements
We've made sure to identify these throughout our repositories for new contributors. 1. Fixes should be titled as follows: **`{IA NAME}: Brief explanation`**. For example: `PeopleInSpace: Use smaller local image, fallback to API when needed.`
- [Goodie Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-goodies/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) ([Goodie docs](https://duck.co/duckduckhack/goodie_overview)) 2. Paste the relevant [Instant Answer Page URL](https://duck.co/ia/new_ia) in the description field. This will automatically link the PR to the Instant Answer.
- [Spice Low Hanging Fruit](https://github.com/duckduckgo/zeroclickinfo-spice/issues?q=is%3Aopen+is%3Aissue+label%3A%22Low-Hanging+Fruit%22) ([Spice docs](https://duck.co/duckduckhack/spice_overview))
**2. Dive in**
Go ahead and comment on any issues you're interested in helping with. Let us know what you're thinking and if you'd like any help or guidance.
As always, feel free to [ask us anything](mailto:open@duckduckgo.com), and don't forget the handy [Instant Answer documentation](https://duck.co/duckduckhack/ddh-intro).
3. Include the issue number in the description (conveniently, this will automatically resolve the issue upon merging). Describe your motivation, thought process, and solution in the description. For example:
"**Fixes #2038.** The images used by the API are very large and don't change often. I've put a smaller version of each image (and a 2x version for retina screens) in the share directory. The callback will try and load a local image based on the astronauts name and fallback to using the API's image if one does not exist."
> **IMPORTANT:** Don't worry if you get an initial error regarding failing Travis tests. The reason is that your IA page hasn't yet been moved out of the "Planning" status - which only community leaders/staff can do. As long as the ID in your IA page matches the ID in your code, and you've pasted the URL to your IA page, you can ignore this initial error.

View File

@ -1,13 +1,71 @@
# DuckDuckHack Goodies [![Build Status](https://travis-ci.org/duckduckgo/zeroclickinfo-goodies.png?branch=master)](https://travis-ci.org/duckduckgo/zeroclickinfo-goodies) # Welcome to DuckDuckHack's Goodies Instant Answers Repository
Join us on Slack! [Request invite](mailto:QuackSlack@duckduckgo.com?subject=AddMe) [![Build Status](https://travis-ci.org/duckduckgo/zeroclickinfo-goodies.png?branch=master)](https://travis-ci.org/duckduckgo/zeroclickinfo-goodies)
This repository contains all the Goodie instant answers. If you are developing a Goodie instant answer you will need to fork this repository. ![](http://docs.duckduckhack.com/assets/hack search engine.png)
**If you would like to contribute to DuckDuckHack, please start by reading the [DuckDuckHack Documentation](https://dukgo.com/duckduckhack/ddh-intro).** **We're a community dedicated to making delightful search results for everyone. We do this using existing APIs, external data sets, or just some well-placed code.**
------ *Instant Answers* appear on DuckDuckGo.com above ads and organic links, on [millions of searches](https://duckduckgo.com/traffic.html). Instant Answers are created by an open source community of developers [around the world](http://duckduckgo.meetup.com/) like yourself. Welcome!
### Goodie Instant Answer Example *You can find the [full documentation here](http://docs.duckduckhack.com).*
![Unit Conversion Example](https://raw.githubusercontent.com/duckduckgo/duckduckgo-documentation/master/duckduckhack/assets/goodie_readme_example.png) ## Jump Right In
The best way to learn about Instant Answers is to dive in with a walkthrough:
- Learn how to build a [convenient calculation tool](http://docs.duckduckhack.com/walkthroughs/calculation.html)
- Learn how to turn an [external API into delightful search results](http://docs.duckduckhack.com/walkthroughs/forum-lookup.html)
- Create a [cheat sheet for a favorite topic](http://docs.duckduckhack.com/walkthroughs/programming-syntax.html)
We also welcome new contributors to learn by improving existing Instant Answers:
- Tackle some of the "low-hanging fruit" and, "high priority" issues on our [Live Issues Page](https://duck.co/ia/dev/issues?tag=lowhangingfruit)
- Help other DuckDuckHackers with their [Instant Answers in development](https://duck.co/ia/dev/pipeline)
- Suggest improvements & file bugs for [live Instant Answers](https://duck.co/ia) through the, "Create Issue" button on each IA page
- Have a [favorite Instant Answer](http://duck.co/ia) that you want to make even better? Feel free to dive in and make changes.
## Say Hello
[![slack](http://docs.duckduckhack.com/assets/slack.png) You should definitely join us on Slack](https://quackslack.herokuapp.com) (you can also [email us](mailto:open@duckduckgo.com)).
P.S.We're a digital community, but real people - we frequently meet up to hack together. Check out our [global meetups](http://duckduckgo.meetup.com/).
## Live Instant Answers Made by the Community
Here are some examples of what contributors have created. You can see [all live Instant Answers listed here](https://duck.co/ia).
Instant Answers can be quite dynamic...
![](http://docs.duckduckhack.com/assets/parking_ny.png)
...or simply convenient:
![](http://docs.duckduckhack.com/assets/sales_tax.png)
Some are just cool:
![](http://docs.duckduckhack.com/assets/heads_tails.png)
Many are absolutely delightful and unexpected:
![](http://docs.duckduckhack.com/assets/bpm_ms.png)
Many are super practical...
![](http://docs.duckduckhack.com/assets/air_quality.png)
...in ways we never imagined:
![](http://docs.duckduckhack.com/assets/blue_pill.png)
Some Instant Answers are built from pure code:
![](http://docs.duckduckhack.com/assets/url_encode.png)
Other Instant Answers channel external sources (API requests):
![App search Instant Answer example](http://docs.duckduckhack.com/assets/app_search_example.png)
The [possibilities are endless](https://duck.co/ia). **Our community's mission is to cover every topic with a community-generated Instant Answer.**

View File

@ -40,7 +40,9 @@ sharedir_method = module_share_dir
version_regexp = ^(.+)$ version_regexp = ^(.+)$
[PkgVersion] [PkgVersion]
[GithubMeta] [GithubMeta]
[@Git] [Git::Check]
[Git::Commit]
[Git::Tag::IAChangelog]
tag_format = %v tag_format = %v
[Git::Push] [Git::Push]

25
lib/DDG/Goodie/ABC.pm Normal file → Executable file
View File

@ -10,16 +10,6 @@ triggers startend => qw/choose pick select/;
zci answer_type => "choice"; zci answer_type => "choice";
zci is_cached => 0; zci is_cached => 0;
primary_example_queries 'choose yes or no';
secondary_example_queries 'choose heads or tails', 'pick this or that or none';
description 'make a random choice';
name 'ABC';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ABC.pm';
category 'random';
topics 'trivia';
attribution cpan => ['CRZEDPSYC','crazedpsyc'],
github => ['kablamo', 'Eric Johnson'];
handle remainder => sub { handle remainder => sub {
my $query = $_; my $query = $_;
@ -52,11 +42,16 @@ handle remainder => sub {
my $operation = $selection_type . ' selection from'; my $operation = $selection_type . ' selection from';
return $selection . " (" . $selection_type . ")", return $selection . " (" . $selection_type . ")",
structured_answer => { structured_answer => {
input => [html_enc($choice_string)], data => {
operation => $operation, title => html_enc("$selection"),
result => html_enc($selection), subtitle => html_enc("$operation: $choice_string")
}; },
templates => {
group => 'text',
moreAt => 0
}
};
}; };
# The query must look like # The query must look like

View File

@ -4,18 +4,6 @@ package DDG::Goodie::AltCalendars;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
primary_example_queries 'heisei 24';
secondary_example_queries 'meiji 1';
description 'Convert non-Gregorian years to the Gregorian calendar';
name 'Alternative Calendars';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/AltCalendars.pm';
category 'conversions';
topics 'everyday';
attribution web => ['http://kyokodaniel.com/tech/', 'Daniel Davis'],
github => ['https://github.com/tagawa', 'tagawa'],
twitter => ['https://twitter.com/ourmaninjapan', 'ourmaninjapan'];
triggers any => 'juche', 'minguo', 'meiji', 'taisho', 'taishou', 'showa', 'shouwa', 'heisei'; triggers any => 'juche', 'minguo', 'meiji', 'taisho', 'taishou', 'showa', 'shouwa', 'heisei';
zci answer_type => 'date_conversion'; zci answer_type => 'date_conversion';
@ -47,8 +35,6 @@ handle query_parts => sub {
return $answer, return $answer,
structured_answer => { structured_answer => {
id => 'altcalendars',
name => 'Calendar Conversion',
data => { data => {
title => $year, title => $year,
subtitle => "$era_name $era_year - Equivalent Gregorian Year" subtitle => "$era_name $era_year - Equivalent Gregorian Year"

View File

@ -25,19 +25,6 @@ triggers start => @triggers;
zci answer_type => "anagram"; zci answer_type => "anagram";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries "anagram of filter";
secondary_example_queries "find anagram for partial men";
description "find the anagram(s) of your query";
name "Anagram";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Anagram.pm";
category "transformations";
topics "words_and_games";
attribution github => ["https://github.com/loganom", 'loganom'],
github => ["https://github.com/beardlybread", "beardlybread"],
github => ['https://github.com/gdrooid', 'gdrooid'],
email => ['gdrooid@openmailbox.org', 'gdrooid'];
# Calculate the frequency of the characters in a string # Calculate the frequency of the characters in a string
sub calc_freq { sub calc_freq {
my ($str) = @_; my ($str) = @_;
@ -102,17 +89,18 @@ handle remainder => sub {
$response = join ', ', sort { $a cmp $b } @output; $response = join ', ', sort { $a cmp $b } @output;
$operation = 'Anagrams of'; $operation = 'Anagrams of';
} else { } else {
do { return;
$response = join '', shuffle split(//, $word);
} while (length($word) > 1 && $response eq $word);
$operation = 'Scrambled letters of';
} }
return $response, return $response,
structured_answer => { structured_answer => {
input => [html_enc($word)], data => {
operation => $operation, title => html_enc($response),
result => html_enc($response) subtitle => $operation . ' ' . html_enc($word)
},
templates => {
group => 'text',
},
}; };
}; };

View File

@ -1,34 +0,0 @@
package DDG::Goodie::Ascii;
# ABSTRACT: ASCII
use strict;
use DDG::Goodie;
triggers end => "ascii";
primary_example_queries '0110100001100101011011000110110001101111 to ascii';
description 'convert binary data to readable characters';
name 'Ascii';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Binary.pm';
category 'transformations';
topics 'cryptography';
zci answer_type => "ascii_conversion";
zci is_cached => 1;
handle remainder => sub {
my $ascii = pack("B*", $1) if /^(([0-1]{8})*)\s+(in|to)$/;
my $binary = $1;
return unless $ascii;
return "$binary in binary is \"$ascii\" in ASCII",
structured_answer => {
input => [$binary],
operation => 'Binary to ASCII',
result => html_enc($ascii),
};
};
1;

View File

@ -9,14 +9,6 @@ triggers start => "aspect ratio";
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "aspect_ratio"; zci answer_type => "aspect_ratio";
primary_example_queries 'aspect ratio 4:3 640:?';
description 'complete the missing value with a given ratio';
name 'AspectRatio';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/AspectRatio.pm';
category 'calculations';
topics 'math';
attribution github => [ 'https://github.com/mrshu', 'mrshu' ];
handle remainder => sub { handle remainder => sub {
my $input = $_; my $input = $_;
my $result = 0; my $result = 0;
@ -35,11 +27,15 @@ handle remainder => sub {
return unless $result; return unless $result;
return "Aspect ratio: $result ($pretty_ratio)", return "Aspect ratio: $result ($pretty_ratio)",
structured_answer => { structured_answer => {
input => [$pretty_ratio], data => {
operation => 'Aspect ratio', title => $result,
result => $result subtitle => 'Aspect ratio: ' . $pretty_ratio,
}; },
templates => {
group => 'text',
}
};
} }
}; };

29
lib/DDG/Goodie/Atbash.pm Normal file → Executable file
View File

@ -4,18 +4,6 @@ package DDG::Goodie::Atbash;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
primary_example_queries 'atbash hello';
secondary_example_queries 'atbash svool';
description 'A simple substitution cipher using a reversed alphabet';
name 'Atbash';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Atbash.pm';
category 'transformations';
topics 'cryptography';
attribution web => ['http://kyokodaniel.com/tech/', 'Daniel Davis'],
github => ['https://github.com/tagawa', 'Daniel Davis'],
twitter => ['https://twitter.com/ourmaninjapan', 'Daniel Davis'];
triggers start => 'atbash'; triggers start => 'atbash';
zci answer_type => 'atbash'; zci answer_type => 'atbash';
@ -27,8 +15,6 @@ handle remainder => sub {
return unless $in_string; return unless $in_string;
my $operation = 'Atbash';
my $result; my $result;
foreach my $char (split //, $in_string) { foreach my $char (split //, $in_string) {
if ($char =~ /([a-z])/) { if ($char =~ /([a-z])/) {
@ -41,11 +27,16 @@ handle remainder => sub {
$result .= $char; $result .= $char;
} }
return "$operation: $result", return "Atbash: $result",
structured_answer => { structured_answer => {
input => [html_enc($in_string)], data => {
operation => $operation, title => "$result",
result => html_enc($result), subtitle => html_enc("Atbash: $in_string")
},
templates => {
group => 'text',
moreAt => 0
}
}; };
}; };

View File

@ -4,23 +4,24 @@ package DDG::Goodie::Average;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
triggers startend => "avg", "average", "mean", "median", "root mean square"; triggers startend => "avg", "average", "mean", "median", "root mean square", "rms";
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "average"; zci answer_type => "average";
primary_example_queries 'average 12, 45, 78, 1234';
secondary_example_queries 'avg 1,2,3', 'root mean square 1,2,3';
description 'take the average of a list of numbers';
name 'Average';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Average.pm';
category 'calculations';
topics 'math';
attribution twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'];
handle remainder => sub { handle remainder => sub {
my $query = $req->query_lc;
my $type;
if ($query =~ m/root mean square|rms/) {
$type = "Root Mean Square";
} elsif ($query =~ m/avg|average|mean/) {
$type = "Mean";
} else {
$type = "Median";
}
#Remove leading/trailing text from list of numbers #Remove leading/trailing text from list of numbers
s/^[a-zA-Z\s]+//; s/^[a-zA-Z\s]+//;
s/\s+[a-zA-Z]+$//; s/\s+[a-zA-Z]+$//;
@ -46,26 +47,39 @@ handle remainder => sub {
# get the length of the array # get the length of the array
my $len = @nums; my $len = @nums;
# calculate the mean my $result;
my $mean = $sum/$len;
# sort the list numerically, least to greatest if ($type eq "Mean") {
@nums = sort { $a <=> $b } @nums; # calculate the mean
my $med; $result = $sum/$len;
if ($len % 2 eq 0) { } elsif ($type eq "Median") {
# get the two middle numbers, since the # sort the list numerically, least to greatest
# length is even, and calculate their mean @nums = sort { $a <=> $b } @nums;
$med = ($nums[$len/2] + $nums[$len/2-1])/2; if ($len % 2 eq 0) {
# get the two middle numbers, since the
# length is even, and calculate their mean
$result = ($nums[$len/2] + $nums[$len/2-1])/2;
} else {
# get the middle number
$result = $nums[int($len/2)]
}
} else { } else {
# get the middle number $result += ($_ ** 2) for @nums;
$med = $nums[int($len/2)] $result /= $len;
$result = sqrt $result;
} }
my $rms; return "$type: $result",
$rms += ($_ ** 2) for @nums; structured_answer => {
$rms /= $len; data => {
$rms = sqrt $rms; title => html_enc($result),
return "Mean: $mean; Median: $med; Root Mean Square: $rms", html => "<div class='average--container'><div><span class='average--key'>Mean:</span> <span class='average--value'>$mean</span></div> <div><span class='average--key'>Median:</span> <span class='average--value'>$med</span></div> <div><span class='average--key'>Root Mean Square:</span> <span class='average--value'>$rms</span></div></div>"; subtitle => $type . ' of: ' . html_enc($_)
},
templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -7,15 +7,6 @@ use DDG::Goodie;
zci answer_type => "bpmto_ms"; zci answer_type => "bpmto_ms";
zci is_cached => 1; zci is_cached => 1;
name "BPM (beats per minute) to ms (milliseconds) converter";
description "Takes a tempo as BPM (beats per minute), eg. 120, and returns the corresponding note values as milliseconds.";
primary_example_queries "120 bpm to ms";
category "conversions";
topics "music";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/BPMToMs.pm";
attribution github => ["https://github.com/stefolof", "stefolof"],
twitter => "stefolof";
triggers end => "bpm to ms", "bpm to milliseconds", "bpm to note values", "bpm to note lengths", "bpm", "bpm timings", "beats per minute to milliseconds", triggers end => "bpm to ms", "bpm to milliseconds", "bpm to note values", "bpm to note lengths", "bpm", "bpm timings", "beats per minute to milliseconds",
"beats per minute to ms", "beats per minute to note values", "beats per minute to note lengths", "beats per minute", "beats per minute timings"; "beats per minute to ms", "beats per minute to note values", "beats per minute to note lengths", "beats per minute", "beats per minute timings";
@ -63,8 +54,6 @@ handle remainder => sub {
return $plaintext, return $plaintext,
structured_answer => { structured_answer => {
id => 'bpmto_ms',
name => 'Music',
data => \@items, data => \@items,
meta => { meta => {
sourceUrl => "https://wikipedia.org/wiki/Tempo#Beats_per_minute", sourceUrl => "https://wikipedia.org/wiki/Tempo#Beats_per_minute",

View File

@ -7,19 +7,6 @@ use DDG::Goodie;
zci answer_type => "brt"; zci answer_type => "brt";
zci is_cached => 1; zci is_cached => 1;
name "BRT";
description "Track a shipment from BRT";
icon_url "https://afelicioni.github.io/assets/i/www.brt.it.ico";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/BRT.pm";
primary_example_queries "brt 123456789012";
secondary_example_queries "brt 1234567890123456789";
source "BRT";
category 'ids';
topics 'special_interest';
attribution web => ["https://afelicioni.github.io/", "Alessio Felicioni"],
github => ["afelicioni", "Alessio Felicioni"],
twitter => ["afelicioni_pro", "Alessio Felicioni"];
triggers start => "brt"; triggers start => "brt";
handle query_lc => sub { handle query_lc => sub {

View File

@ -6,6 +6,10 @@ use DDG::Goodie;
use Math::Int2Base qw(int2base base2int); use Math::Int2Base qw(int2base base2int);
use 5.010; use 5.010;
zci answer_type => "conversion";
zci is_cached => 1;
use bigint; use bigint;
my %base_map = ( my %base_map = (
@ -40,7 +44,6 @@ topics 'math';
attribution web => [ 'http://perlgeek.de/blog-en', 'Moritz Lenz' ], attribution web => [ 'http://perlgeek.de/blog-en', 'Moritz Lenz' ],
github => [ 'http://github.com/moritz', 'Moritz Lenz']; github => [ 'http://github.com/moritz', 'Moritz Lenz'];
handle query_clean => sub { handle query_clean => sub {
return unless /^(?<la>$prefix_keys)?(?<inp>[0-9A-Za-z]+)\s*((?:(?:in|as)\s+)?(?:(?<lt>$map_keys)|(?:base\s*(?<ln>[0-9]+)))\s+)?(?:(?:in|as|to)\s+)?(?:(?<rt>$map_keys)|(?:base\s*(?<rn>[0-9]+)))$/; return unless /^(?<la>$prefix_keys)?(?<inp>[0-9A-Za-z]+)\s*((?:(?:in|as)\s+)?(?:(?<lt>$map_keys)|(?:base\s*(?<ln>[0-9]+)))\s+)?(?:(?:in|as|to)\s+)?(?:(?<rt>$map_keys)|(?:base\s*(?<rn>[0-9]+)))$/;
my $input = uc $+{'inp'}; # uc is necessary as Int2Base doesnt support lowercase my $input = uc $+{'inp'}; # uc is necessary as Int2Base doesnt support lowercase
@ -60,10 +63,14 @@ handle query_clean => sub {
return "$input in base $to_base is $output", return "$input in base $to_base is $output",
structured_answer => { structured_answer => {
input => ["$input"], data => {
operation => "From base $from_base to base $to_base", title => $output,
result => $output subtitle => "From base $from_base to base $to_base: $input"
}; },
templates => {
group => 'text'
}
};
}; };
sub convert_base { sub convert_base {

View File

@ -13,23 +13,6 @@ triggers start => "base64";
zci answer_type => "base64_conversion"; zci answer_type => "base64_conversion";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'base64 encode foo';
secondary_example_queries 'base64 decode dGhpcyB0ZXh0';
description 'encode to and decode from base64';
name 'Base64';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Base64.pm';
category 'conversions';
topics 'programming';
attribution web => ['robert.io', 'Robert Picard'],
github => ['http://github.com/rpicard', 'Robert Picard'],
twitter => ['http://twitter.com/__rlp', 'Robert Picard'],
web => ['http://robb.weblaws.org/about', 'Robb Shecter'],
github => ['http://github.com/dogweather', 'Robb Shecter'],
twitter => ['http://twitter.com/dogweather', 'Robb Shecter'];
handle remainder => sub { handle remainder => sub {
my ($command, $input) = query_components($_); my ($command, $input) = query_components($_);
return unless $input; return unless $input;
@ -37,13 +20,16 @@ handle remainder => sub {
my $output = perform_conversion($command, $input); my $output = perform_conversion($command, $input);
my $operation = 'Base64 ' . $command; my $operation = 'Base64 ' . $command;
my $text_output = $operation . 'd: ' . $output; return $operation . 'd: ' . $output,
return $text_output,
structured_answer => { structured_answer => {
input => [html_enc($input)], data => {
operation => $operation, title => html_enc($output),
result => html_enc($output), subtitle => $operation . ': ' . html_enc($input)
}; },
templates => {
group => 'text'
}
};
}; };

View File

@ -1,4 +1,5 @@
package DDG::Goodie::BashPrimaryExpressions; package DDG::Goodie::BashPrimaryExpressions;
# ABSTRACT: human-readable descriptions of bash shell expressions # ABSTRACT: human-readable descriptions of bash shell expressions
use strict; use strict;
@ -7,95 +8,109 @@ use warnings;
use DDG::Goodie; use DDG::Goodie;
triggers startend => 'bash if', 'bash'; triggers startend => 'bash if', 'bash';
primary_example_queries 'bash [ -z hello ]';
secondary_example_queries 'bash if [[ "abc" -lt "cba" ]]';
description 'Bash Primary Expressions Help';
name 'Bash Help';
source 'http://tille.garrels.be/training/bash/ch07.html';
attribution github => [ 'http://github.com/mintsoft', 'Rob Emery' ];
category 'computing_tools';
topics 'sysadmin';
zci answer_type => 'expression_description'; zci answer_type => 'expression_description';
zci is_cached => 1; zci is_cached => 1;
our %if_description = ( our %if_description = (
'-a' => "true if ARG2 exists", '-a' => ["true if ", "ARG2", " exists"],
'-b' => "true if ARG2 exists and is a block-special file", '-b' => ["true if ", "ARG2", " exists and is a block-special file"],
'-c' => "true if ARG2 exists and is a character-special file", '-c' => ["true if ", "ARG2", " exists and is a character-special file"],
'-d' => "true if ARG2 exists and is a directory", '-d' => ["true if ", "ARG2", " exists and is a directory"],
'-e' => "true if ARG2 exists", '-e' => ["true if ", "ARG2", " exists"],
'-f' => "true if ARG2 exists and is a regular file", '-f' => ["true if ", "ARG2", " exists and is a regular file"],
'-g' => "true if ARG2 exists and its SGID bit is set", '-g' => ["true if ", "ARG2", " exists and its SGID bit is set"],
'-h' => "true if ARG2 exists and is a symbolic link", '-h' => ["true if ", "ARG2", " exists and is a symbolic link"],
'-k' => "true if ARG2 exists and its sticky bit is set", '-k' => ["true if ", "ARG2", " exists and its sticky bit is set"],
'-p' => "true if ARG2 exists and is a named pipe (FIFO)", '-p' => ["true if ", "ARG2", " exists and is a named pipe (FIFO)"],
'-r' => "true if ARG2 exists and is readable", '-r' => ["true if ", "ARG2", " exists and is readable"],
'-s' => "true if ARG2 exists and has a size greater than zero", '-s' => ["true if ", "ARG2", " exists and has a size greater than zero"],
'-t' => "true if ARG2 descriptor FD is open and refers to a terminal", '-t' => ["true if ", "ARG2", " descriptor FD is open and refers to a terminal"],
'-u' => "true if ARG2 exists and its SUID (set user ID) bit is set", '-u' => ["true if ", "ARG2", " exists and its SUID (set user ID) bit is set"],
'-w' => "true if ARG2 exists and is writable", '-w' => ["true if ", "ARG2", " exists and is writable"],
'-x' => "true if ARG2 exists and is executable", '-x' => ["true if ", "ARG2", " exists and is executable"],
'-O' => "true if ARG2 exists and is owned by the effective user ID", '-O' => ["true if ", "ARG2", " exists and is owned by the effective user ID"],
'-G' => "true if ARG2 exists and is owned by the effective group ID", '-G' => ["true if ", "ARG2", " exists and is owned by the effective group ID"],
'-L' => "true if ARG2 exists and is a symbolic link", '-L' => ["true if ", "ARG2", " exists and is a symbolic link"],
'-N' => "true if ARG2 exists and has been modified since it was last read", '-N' => ["true if ", "ARG2", " exists and has been modified since it was last read"],
'-S' => "true if ARG2 exists and is a socket", '-S' => ["true if ", "ARG2", " exists and is a socket"],
'-o' => "true if shell option ARG2 is enabled", '-o' => ["true if shell option ", "ARG2", " is enabled"],
'-z' => "true if the length of 'ARG2' is zero", '-z' => ["true if the length of '", "ARG2", "' is zero"],
'-n' => "true if the length of 'ARG2' is non-zero", '-n' => ["true if the length of '", "ARG2", "' is non-zero"],
'==' => "true if the strings ARG1 and ARG2 are equal", '==' => ["true if the strings ", "ARG1", " and ", "ARG2", " are equal"],
'!=' => "true if the strings ARG1 and ARG2 are not equal", '!=' => ["true if the strings ", "ARG1", " and ", "ARG2", " are not equal"],
'<' => "true if ARG1 string-sorts before ARG2 in the current locale", '<' => ["true if ", "ARG1", " string-sorts before ", "ARG2", " in the current locale"],
'>' => "true if ARG1 string-sorts after ARG2 in the current locale", '>' => ["true if ", "ARG1", " string-sorts after ", "ARG2", " in the current locale"],
'-eq' => "true if ARG1 and ARG2 are numerically equal", '-eq' => ["true if ", "ARG1", " and ", "ARG2", " are numerically equal"],
'-ne' => "true if ARG1 and ARG2 are not numerically equal", '-ne' => ["true if ", "ARG1", " and ", "ARG2", " are not numerically equal"],
'-lt' => "true if ARG1 is numerically less than ARG2", '-lt' => ["true if ", "ARG1", " is numerically less than ", "ARG2"],
'-le' => "true if ARG1 is numerically less than or equal to ARG2", '-le' => ["true if ", "ARG1", " is numerically less than or equal to ", "ARG2"],
'-gt' => "true if ARG1 is numerically greater than ARG2", '-gt' => ["true if ", "ARG1", " is numerically greater than ", "ARG2"],
'-ge' => "true if ARG1 is numerically greater than or equal to ARG2", '-ge' => ["true if ", "ARG1", " is numerically greater than or equal to ", "ARG2"],
'-nt' => "true if ARG1 has been changed more recently than ARG2 or if ARG1 exists and ARG2 does not", '-nt' => ["true if ", "ARG1", " has been changed more recently than ", "ARG2", " or if ", "ARG1", " exists and ", "ARG2", " does not"],
'-ot' => "true if ARG1 is older than ARG2 or ARG2 exists and ARG1 does not", '-ot' => ["true if ", "ARG1", " is older than ", "ARG2", " or ", "ARG2", " exists and ", "ARG1", " does not"],
'-ef' => "true if ARG1 and ARG2 refer to the same device and inode numbers" '-ef' => ["true if ", "ARG1", " and ", "ARG2", " refer to the same device and inode numbers"]
); );
# This checks if our arguments in the `if` statement are valid.
my $re_args = '[^\[\]\s]+|".+"|\'.+\'';
my $re = qr/^
(?:if\s)? # Match `if` if there is one.
[\[]{1,2}\s # Match `[[` inside the `if` statement.
([!]\s)? # Capture the `!` in queries such as `bash if [ ! "something" -gt "150" ]`
(?:($re_args)\s)? # Capture the first argument in the `if`
(-[a-zA-Z]{1,2}|[<>]|[!=]{1,2})\s # Capture the options such as `-lt` for less than.
($re_args)\s # Capture the second argument.
[\]]{1,2} # Match `]]` that ends the `if` statement.
$/x;
handle remainder => sub { handle remainder => sub {
# This checks if our arguments in the `if` statement are valid.
my $re_args = '[^\[\]\s]+|".+"|\'.+\'';
my $re = qr/^
(?:if\s)? # Match `if` if there is one.
[\[]{1,2}\s # Match `[[` inside the `if` statement.
([!]\s)? # Capture the `!` in queries such as `bash if [ ! "something" -gt "150" ]`
(?:($re_args)\s)? # Capture the first argument in the `if`
(-[a-zA-Z]{1,2}|[<>]|[!=]{1,2})\s # Capture the options such as `-lt` for less than.
($re_args)\s # Capture the second argument.
[\]]{1,2} # Match `]]` that ends the `if` statement.
$/x;
my ($not, $left_arg, $op, $right_arg) = ($_ =~ $re); my ($not, $left_arg, $op, $right_arg) = ($_ =~ $re);
return unless ($op && $right_arg); return unless ($op && $right_arg);
return unless $if_description{$op}; return unless $if_description{$op};
my $text_output = $if_description{$op}; my $text_output = join("",@{$if_description{$op}});
$text_output =~ s/^true/false/ if $not; $text_output =~ s/^true/false/ if $not;
my $html_output = html_enc($text_output); if ($left_arg) {
my $html_right_arg = html_enc($right_arg); $text_output =~ s/ARG1/$left_arg/g;
}
if ($left_arg) { $text_output =~ s/ARG2/$right_arg/g;
my $html_left_arg = html_enc($left_arg);
$text_output =~ s/ARG1/$left_arg/g;
$html_output =~ s/ARG1/<pre>$html_left_arg<\/pre>/g;
}
$text_output =~ s/ARG2/$right_arg/g; my @results;
$html_output =~ s/ARG2/<pre>$html_right_arg<\/pre>/g; foreach my $el(@{$if_description{$op}}) {
my $temp;
if ($el eq 'ARG1') {
$temp->{text} = "";
$temp->{value} = $left_arg;
} elsif ($el eq 'ARG2') {
$temp->{text} = "";
$temp->{value} = $right_arg;
} else {
$temp->{text} = $el;
$temp->{value} = "";
}
push @results, $temp;
}
my $intro = "The Bash expression <pre>" . html_enc($_) . "</pre> results to"; my $intro = "The Bash expression $_ results to";
return "$intro $text_output.", html => "$intro $html_output.", heading => html_enc($_) . " (Bash)"; return "$intro $text_output.",
structured_answer => {
data => {
intro => $_,
results => \@results
},
templates => {
group => 'text',
item => 0,
options => {
content => 'DDH.bash_primary_expressions.content'
}
}
};
}; };
1; 1;

View File

@ -0,0 +1,29 @@
package DDG::Goodie::BeamMeUpScotty;
# ABSTRACT: A Star Trek catchphrase
use DDG::Goodie;
use strict;
zci answer_type => 'beam_me_up_scotty';
zci is_cached => 1;
triggers start => 'beam me up scotty', 'beam us up scotty';
handle remainder => sub {
return unless $_ eq '';
my $answer = 'Aye, aye, captain.';
return $answer,
structured_answer => {
data => {
title => $answer,
subtitle => 'Code phrase: Beam me up, Scotty',
},
templates => {
group => 'text',
},
};
};
1;

View File

@ -0,0 +1,140 @@
package DDG::Goodie::Bin2Unicode;
# ABSTRACT: Convert binary to unicode
use DDG::Goodie;
use strict;
no warnings qw'non_unicode overflow portable';
zci answer_type => 'bin2unicode';
zci is_cached => 1;
triggers query => qr{^([01\s]{8,})(?:\s+(?:to\s+)?(?:unicode|text|ascii))?$};
my $MAX_CODE_PT = 1114111;
my %ctrl_chars = (
0 => 'Null character (NUL)',
1 => 'Start of Heading (SOH)',
2 => 'Start of Text (STX)',
3 => 'End-of-text character (ETX)',
4 => 'End-of-transmission character (EOT)',
5 => 'Enquiry character (ENQ)',
6 => 'Acknowledge character (ACK)',
7 => 'Bell character (BEL)',
8 => 'Backspace (BS)',
9 => 'Horizontal tab (HT)',
10 => 'Line feed (LF)',
11 => 'Vertical tab (VT)',
12 => 'Form feed (FF)',
13 => 'Carriage return (CR)',
14 => 'Shift Out (SO)',
15 => 'Shift In (SI)',
16 => 'Data Link Escape (DLE)',
17 => 'Device Control 1 (DC1)',
18 => 'Device Control 2 (DC2)',
19 => 'Device Control 3 (DC3)',
20 => 'Device Control 4 (DC4)',
21 => 'Negative-acknowledge character (NAK)',
22 => 'Synchronous Idle (SYN)',
23 => 'End of Transmission Block (ETB)',
24 => 'Cancel character (CAN)',
25 => 'End of Medium (EM)',
26 => 'Substitute character (SUB)',
27 => 'Escape character (ESC)',
28 => 'File Separator (FS)',
29 => 'Group Separator (GS)',
30 => 'Record Separator (RS)',
31 => 'Unit Separator (US)',
32 => 'Space (SP)',
127 => 'Delete (DEL)',
128 => 'Padding Character (PAD)',
129 => 'High Octet Preset (HOP)',
130 => 'Break Permitted Here (BPH)',
131 => 'No Break Here (NBH)',
132 => 'Index (IND)',
133 => 'Next Line (NEL)',
134 => 'Start of Selected Area (SSA)',
135 => 'End of Selected Area (ESA)',
136 => 'Character Tabulation Set (HTS)',
137 => 'Character Tabulation with Justification (HTJ)',
138 => 'Line Tabulation Set (VTS)',
139 => 'Partial Line Forward (PLD)',
140 => 'Partial Line Backward (PLU)',
141 => 'Reverse Line Feed (RI)',
142 => 'Single-Shift Two (SS2)',
143 => 'Single-Shift Three (SS3)',
144 => 'Device Control String (DCS)',
145 => 'Private Use 1 (PU1)',
146 => 'Private Use 2 (PU2)',
147 => 'Set Transmit State (STS)',
148 => 'Cancel character (CCH)',
149 => 'Message Waiting (MW)',
150 => 'Start of Protected Area (SPA)',
151 => 'End of Protected Area (EPA)',
152 => 'Start of String (SOS)',
153 => 'Single Graphic Character Introducer (SGCI)',
154 => 'Single Character Intro Introducer (SCI)',
155 => 'Control Sequence Introducer (CSI)',
156 => 'String Terminator (ST)',
157 => 'Operating System Command (OSC)',
158 => 'Private Message (PM)',
159 => 'Application Program Command (APC)'
);
my $zaahirs_hideout = '0' x 48;
handle matches => sub {
my $q = $_; # orginal query
my $bin_string = shift @_; # captured binary string
my $str;
if($bin_string eq $zaahirs_hideout){
$str = q{Congratulations, you've discovered Zaahir's hideout!};
goto DONE;
}
my $want_ascii = $q =~ /\bascii\b/;
my @bins = $bin_string =~ /([01]+|\s+)/g;
for my $b (@bins){
if($b =~ /^[01]+$/){
return if length($b) % 8;
# Overflow/non-portable warnings expected
my $i = oct("0b$b");
if((exists $ctrl_chars{$i}) && (@bins == 1)){
$str = $ctrl_chars{$i};
$str = "Control character: $str" unless ($i == 32) || ($i == 127);
last;
}
# Assume ascii if out of range or explicitly requested.
# This will work for characters all in the same string
# but will not print the right non-ascii characters *if*
# they are also in the string.
$str .= (($i > $MAX_CODE_PT) || $want_ascii)
? pack('B*', $b)
: chr($i);
}
else {
$str .= $b;
}
}
# return if all control/space (https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes)
return if $str =~ /^[\p{Control} ]+$/;
DONE:
return "Binary '$bin_string' converted to " . $want_ascii ? 'ascii' : 'unicode' . " is '$str'",
structured_answer => {
data => {
title => $str,
subtitle => "Input: $q",
},
templates => {
group => 'text',
moreAt => 0
}
};
};
1;

View File

@ -11,14 +11,6 @@ triggers end => "binary";
zci answer_type => "binary_conversion"; zci answer_type => "binary_conversion";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'foo in binary', '12 as binary', 'hex 0xffff into binary';
secondary_example_queries '0x1e to binary';
description 'convert ASCII, numbers, and hex to binary';
name 'Binary';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Binary.pm';
category 'conversions';
topics 'geek';
sub bin { sub bin {
my @tex = shift; my @tex = shift;
my $bin; my $bin;
@ -51,32 +43,49 @@ sub bin2dec {
handle remainder => sub { handle remainder => sub {
my @out; my ($input, $from, $to, $result);
if (/^([01]+)(\s+from)?$/) { if (/^([01]+)(\s+from)?$/) {
# Looks like they gave us some binary, let's turn it into decimal! # Looks like they gave us some binary, let's turn it into decimal!
@out = ($1, bin2dec($1), "Binary", "Decimal"); $input = $1;
$from = "Binary";
$to = "Decimal";
$result = bin2dec($1);
} elsif (s/\s+(in|to|into|as)$//) { } elsif (s/\s+(in|to|into|as)$//) {
# Looks like they are asking for a conversion to binary # Looks like they are asking for a conversion to binary
# So, try to figure out what they've got. # So, try to figure out what they've got.
# They can either tell us explicitly or we can try to just work it out. # They can either tell us explicitly or we can try to just work it out.
if (/^(?:decimal\s+)?([0-9]+)$/) { if (/^(?:decimal\s+)?([0-9]+)$/) {
@out = ($1, dec2bin($1), "Decimal", "Binary"); $input = $1;
$from = "Decimal";
$to = "Binary";
$result = dec2bin($1);
} elsif (/^(?:hex\s+)?(?:0x|Ox|x)?([0-9a-fA-F]+)$/) { } elsif (/^(?:hex\s+)?(?:0x|Ox|x)?([0-9a-fA-F]+)$/) {
# Normalize the hex output with lowercase and a prepended '0x'. # Normalize the hex output with lowercase and a prepended '0x'.
@out = ('0x' . lc $1, hex2bin($1), "Hex", "Binary"); $input = '0x' . lc $1;
$from = "Hex";
$to = "Binary";
$result = hex2bin($1);
} else { } else {
# We didn't match anything else, so just convert whatever string is left. # We didn't match anything else, so just convert whatever string is left.
@out = ($_, bin($_), "String", "Binary"); $input = $_;
$from = "String";
$to = "Binary";
$result = bin($_);
} }
} }
return unless (@out); # Didn't hit any conditions, must not be us. return unless ($input); # Didn't hit any conditions, must not be us.
return qq/Binary conversion: $out[0] ($out[2]) = $out[1] ($out[3])/, return qq/Binary conversion: $input ($from) = $result ($to)/,
structured_answer => { structured_answer => {
input => [$out[0]], data => {
operation => $out[2] . ' to ' . $out[3], title => $result,
result => $out[1]}; subtitle =>$from . ' to ' . $to . ': ' . $input
},
templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -17,24 +17,6 @@ triggers query_raw => qr/¬.*/;
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "binary_logic"; zci answer_type => "binary_logic";
attribution
github => ['https://github.com/MithrandirAgain', 'MithrandirAgain'],
github => ['https://github.com/bpaschen', 'Bjoern Paschen'],
twitter => ['https://twitter.com/Prypjat', 'Bjoern Paschen'],
github => ['https://github.com/Sloff', 'Sloff'];
primary_example_queries '4 xor 5', '3 and 2', '1 or 1234';
secondary_example_queries
'9489 xor 394 xor 9349 xor 39 xor 29 xor 4967 xor 3985',
'10 and 12',
'34 or 100',
'10 and (30 or 128)',
'0x01 or not 0X100';
description 'take two numbers and do bitwise logical operations (exclusive-or, or, and, not) on them';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/BinaryLogic.pm';
category 'calculations';
topics 'math';
my $rules = <<'END_OF_GRAMMAR'; my $rules = <<'END_OF_GRAMMAR';
:default ::= action => ::first :default ::= action => ::first
:start ::= Expression :start ::= Expression
@ -143,8 +125,6 @@ handle query_raw => sub {
} }
return $text_output, structured_answer => { return $text_output, structured_answer => {
id => 'binary_logic',
name => 'Answer',
data => { data => {
title => $text_output, title => $text_output,
subtitle => $subtitle subtitle => $subtitle

View File

@ -9,14 +9,6 @@ triggers startend => 'birthstone', 'birth stone';
zci answer_type => "birth_stone"; zci answer_type => "birth_stone";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'birthstone april';
secondary_example_queries 'may birth stone';
description 'returns the birth stone of the specified month';
name 'BirthStone';
topics 'special_interest', 'entertainment';
category 'random';
attribution github => ['https://github.com/austinheimark', 'Austin Heimark'];
my %birthstones = ( my %birthstones = (
"January" => "Garnet", "January" => "Garnet",
"February" => "Amethyst", "February" => "Amethyst",
@ -40,11 +32,15 @@ handle remainder => sub {
return unless $stone; return unless $stone;
return $month . " birthstone: $stone", return $month . " birthstone: $stone",
structured_answer => { structured_answer => {
input => [$month], data => {
operation => 'Birthstone', title => $stone,
result => $stone subtitle => 'Birthstone for '.$month
}; },
templates => {
group => "text",
}
}
}; };
1; 1;

View File

@ -9,16 +9,6 @@ use Math::BigInt;
zci answer_type => "bitsum"; zci answer_type => "bitsum";
zci is_cached => 1; zci is_cached => 1;
name "Bitsum";
description "Computes the Hamming Weight / bit-wise sum of a decimal or hex number.";
primary_example_queries "bitsum 1023", "bitsum 0x789abcd";
secondary_example_queries "hammingweight 1023", "hw 1023";
category "programming";
topics "programming", "cryptography";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Bitsum.pm";
attribution github => ["kste", "Stefan Koelbl"],
twitter => "kste_";
# Triggers # Triggers
triggers start => "bitsum", "hammingweight", "hw", triggers start => "bitsum", "hammingweight", "hw",
"bitsum of", "hammingweight of", "hw of", "bitsum of", "hammingweight of", "hw of",
@ -33,25 +23,38 @@ handle remainder => sub {
my $input_number = $_; my $input_number = $_;
my $hex_number; my $hex_number;
my $binstring; my $binstring;
my $base;
my $base_string;
# Construct binary representation for both hex and decimal numbers # Construct binary representation for both hex and decimal numbers
if( $input_number =~ /^0x/) { if( $input_number =~ /^0x/) {
$hex_number = substr($input_number, 2); $hex_number = substr($input_number, 2);
$binstring = unpack ('B*', pack ('H*',$hex_number)); $binstring = unpack ('B*', pack ('H*',$hex_number));
$base = 16;
$base_string = 'Hexadecimal';
} elsif( $input_number =~ /^0b/) { } elsif( $input_number =~ /^0b/) {
$binstring = substr($input_number, 2); $binstring = substr($input_number, 2);
$base = 2;
$base_string = 'Binary';
} else { } else {
$binstring = Math::BigInt->new($input_number)->as_bin(); $binstring = Math::BigInt->new($input_number)->as_bin();
$base = 10;
$base_string = 'Decimal';
} }
# Count ones # Count ones
my $result = $binstring =~ tr/1/1/; my $result = $binstring =~ tr/1/1/;
$input_number .= ' (Base '.$base.', '.$base_string.')';
return $result, return $result,
structured_answer => { structured_answer => {
input => [html_enc($input_number)], data => {
operation => 'Hamming Weight', title => html_enc($result),
result => html_enc($result), subtitle => 'Hamming Weight Calculation: ' . html_enc($input_number)
},
templates => {
group => 'text'
}
}; };
}; };

View File

@ -14,15 +14,6 @@ triggers startend => 'donor compatibility', 'donor', 'donors for',
zci answer_type => "blood_donor"; zci answer_type => "blood_donor";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'donor O+';
secondary_example_queries 'donor AB+';
description 'Donor types for a given blood type';
name 'BloodDonor';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/BloodDonor.pm';
category 'special';
topics 'everyday';
attribution github => ['https://github.com/faraday', 'faraday'];
my %typeMap = ( my %typeMap = (
'A' => 'A,O', 'A' => 'A,O',
'O' => 'O', 'O' => 'O',
@ -32,7 +23,7 @@ my %typeMap = (
handle remainder => sub { handle remainder => sub {
return unless ($_ =~ /^(O|A|B|AB)(\-|\+)$/i); return unless ($_ =~ /^(O|A|B|AB)((\-|\+)|(\-ve|\+ve))$/i);
my $type = uc $1; my $type = uc $1;
my $rh = $2; my $rh = $2;
@ -49,6 +40,9 @@ handle remainder => sub {
# only when access to same Rh is impossible # only when access to same Rh is impossible
push(@criticalResults, $donorType . '-'); push(@criticalResults, $donorType . '-');
} }
if($rh eq '+ve') {
push(@criticalResults, $donorType . '-ve');
}
} }
my $idealStr = join(' or ', @idealResults); my $idealStr = join(' or ', @idealResults);
@ -60,7 +54,7 @@ handle remainder => sub {
); );
my @record_keys = ("Ideal donor", "Other donors"); my @record_keys = ("Ideal donor", "Other donors");
if($rh eq '+') { if($rh eq '+ve' || $rh eq '+') {
push @record_keys,"Only if no Rh(+) found"; push @record_keys,"Only if no Rh(+) found";
$record_data{"Only if no Rh(+) found"} = $criticalStr; $record_data{"Only if no Rh(+) found"} = $criticalStr;
} }
@ -72,8 +66,6 @@ handle remainder => sub {
} }
return to_text(\%record_data, \@record_keys), structured_answer => { return to_text(\%record_data, \@record_keys), structured_answer => {
id => 'blood_donor',
name => 'Blood Donors',
description => 'Returns available donors for a blood type', description => 'Returns available donors for a blood type',
meta => { meta => {
sourceName => 'Wikipedia', sourceName => 'Wikipedia',

View File

@ -7,7 +7,7 @@ use DDG::Goodie;
use Convert::Braille; use Convert::Braille;
use utf8; use utf8;
triggers query_raw => qr/\p{Braille}|braille/i; triggers query_raw => qr/\p{Braille}|( in| to){1} braille$|^braille:/i;
zci is_cached => 1; zci is_cached => 1;
@ -16,22 +16,29 @@ my $braille_space = ''; # the braille unicode space (U+2800)
handle query_raw => sub { handle query_raw => sub {
my $query = $_; my $query = $_;
$query =~ s/translate to braille |( in)? braille$|^braille //; $query =~ s/( in| to){1} braille$|^braille:\s?//;
return unless $query; return unless $query;
my $result; my $response;
my $type;
if ($query =~ /\p{Braille}/) { if ($query =~ /\p{Braille}/) {
$result = join(" ", map { lc(brailleDotsToAscii($_)) } split(/$braille_space/, $query)); $response = join(" ", map { lc(brailleDotsToAscii($_)) } split(/$braille_space/, $query));
$type = "Ascii/Unicode";
} else { } else {
$result = join($braille_space, map { brailleAsciiToUnicode(uc $_) } split(/\s/, $query)); $response = join($braille_space, map { brailleAsciiToUnicode(uc $_) } split(/\s/, $query));
$type = "Braille";
} }
return $result . ' (Braille)', return $response,
structured_answer => { structured_answer => {
input => [html_enc($query)], data => {
operation => 'Braille translation', title => $response,
result => html_enc($result), subtitle => 'Braille translation: ' . html_enc($query),
},
templates => {
group => 'text',
},
}; };
}; };

105
lib/DDG/Goodie/CaesarCipher.pm Executable file
View File

@ -0,0 +1,105 @@
package DDG::Goodie::CaesarCipher;
# ABSTRACT: Perform a simple Caesar cipher on some text.
use strict;
use DDG::Goodie;
triggers startend => "caesar cipher",
"ceasar cipher",
"shift cipher";
triggers start => 'caesar', 'ceasar';
zci is_cached => 1;
zci answer_type => "caesar_cipher";
my @alphabet = ('a' ... 'z');
my $string_alphabet = join '', @alphabet;
sub build_infobox_element {
my $query = shift;
my @split = split ' ', $query;
return {
label => $query,
url => 'https://duckduckgo.com/?q=' . (join '+', @split) . '&ia=answer',
};
}
my $infobox = [ { heading => "Example Queries", },
build_infobox_element('caesar cipher 2 text'),
build_infobox_element('shift cipher -2 vgzv'),
build_infobox_element('caesar cipher 33 secret'),
build_infobox_element('caesar cipher -7 zljyla'),
];
my @description_pars = split "\n\n",
share('description.txt')->slurp();
my $decode_response = {
data => {
title => "How to decode the caesar cipher",
infoboxData => $infobox,
description_pars => \@description_pars,
},
meta => {
sourceUrl => 'https://en.wikipedia.org/wiki/Caesar_cipher',
sourceName => 'Wikipedia',
},
templates => {
group => 'info',
options => {
content => 'DDH.caesar_cipher.content',
chompContent => 1,
},
},
};
sub wants_decode {
my $query = shift;
return $query =~ /^how to ((de|en)(code|crypt)|use)( (a|the))?|(de|en)(coder?|crypt(er)?)$/i;
}
sub perform_caesar {
my ($to_cipher, $shift_val) = @_;
my $amount = $shift_val % 26;
# This creates the cipher by shifting the alphabet.
my @shifted_alphabet = (@alphabet[$amount...25],
@alphabet[0..$amount-1]);
my $result;
foreach my $char (split //, $to_cipher) {
if ($char =~ /[[:alpha:]]/) {
my $uppercase = $char =~ /[A-Z]/;
my $idx = index $string_alphabet, lc $char;
$char = $shifted_alphabet[$idx] if ($idx != -1);
$char = uc $char if ($uppercase);
}
$result .= $char;
}
return $result;
}
handle remainder => sub {
my $remainder = shift;
my $wants_decode = wants_decode($remainder);
return "Caesar Cipher", structured_answer => $decode_response if $wants_decode;
return unless $remainder =~ /(\-?\d+)\s+([[:ascii:]]+)$/;
my ($shift_val, $to_cipher) = ($1, $2);
my $result = perform_caesar($to_cipher, $shift_val);
return "$result",
structured_answer => {
data => {
title => "$result",
subtitle => html_enc("Caesar cipher $shift_val $to_cipher"),
},
templates => {
group => 'text',
moreAt => 0,
},
};
};
1;

View File

@ -5,14 +5,6 @@ use strict;
use DDG::Goodie; use DDG::Goodie;
use Lingua::EN::Numericalize; use Lingua::EN::Numericalize;
primary_example_queries 'square root of 9';
description 'calculate the nth root';
name 'CalcRoots';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodie/blob/master/lib/DDG/Goodie/CalcRoots.pm';
attribution github => ['https://github.com/duckduckgo', 'duckduckgo'];
category 'calculations';
topics 'math';
triggers any => 'root'; triggers any => 'root';
zci is_cached => 1; zci is_cached => 1;

View File

@ -7,21 +7,12 @@ with 'DDG::GoodieRole::NumberStyler';
use List::Util qw( max ); use List::Util qw( max );
use Math::Trig; use Math::Trig;
use Math::BigInt;
use utf8; use utf8;
zci answer_type => "calc"; zci answer_type => "calc";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries '$3.43+$34.45';
secondary_example_queries '64*343';
description 'Basic calculations';
name 'Calculator';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Calculator.pm';
category 'calculations';
topics 'math';
attribution github => ['https://github.com/duckduckgo', 'duckduckgo'],
github => ['https://github.com/phylum', 'Daniel Smith'];
triggers query_nowhitespace => qr< triggers query_nowhitespace => qr<
^ ^
( what is | calculate | solve | math )? ( what is | calculate | solve | math )?
@ -33,7 +24,7 @@ triggers query_nowhitespace => qr<
[\( \) x X × * % + ÷ / \^ 0-9 \. , _ \$ -]* [\( \) x X × * % + ÷ / \^ 0-9 \. , _ \$ -]*
(?(1) (?: -? [0-9 \. , _ ]+ |) |) (?(1) (?: -? [0-9 \. , _ ]+ |) |)
(?: [\( \) x X × * % + ÷ / \^ \$ -] | times | divided by | plus | minus | cos | sin | tan | cotan | log | ln | log[_]?\d{1,3} | exp | tanh | sec | csc | squared | sqrt | pi | e )+ (?: [\( \) x X × * % + ÷ / \^ \$ -] | times | divided by | plus | minus | fact | factorial | cos | sin | tan | cotan | log | ln | log[_]?\d{1,3} | exp | tanh | sec | csc | squared | sqrt | pi | e )+
(?: [0-9 \. ,]* ) (?: [0-9 \. ,]* )
(?: gross | dozen | pi | e | c | squared | score |) (?: gross | dozen | pi | e | c | squared | score |)
@ -59,6 +50,7 @@ my %named_operations = (
'÷' => '/', '÷' => '/',
'ln' => 'log', # perl log() is natural log. 'ln' => 'log', # perl log() is natural log.
'squared' => '**2', 'squared' => '**2',
'fact' => 'Math::BigInt->new',
); );
my %named_constants = ( my %named_constants = (
@ -85,10 +77,12 @@ handle query_nowhitespace => sub {
return if ($query =~ /^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/); # Probably are searching for a phone number, not making a calculation return if ($query =~ /^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/); # Probably are searching for a phone number, not making a calculation
$query =~ s/^(?:whatis|calculate|solve|math)//; $query =~ s/^(?:whatis|calculate|solve|math)//;
$query =~ s/(factorial)/fact/; #replace factorial with fact
# Grab expression. # Grab expression.
my $tmp_expr = spacing($query, 1); my $tmp_expr = spacing($query, 1);
return if $tmp_expr eq $query; # If it didn't get spaced out, there are no operations to be done. return if $tmp_expr eq $query; # If it didn't get spaced out, there are no operations to be done.
# First replace named operations with their computable equivalents. # First replace named operations with their computable equivalents.
@ -98,7 +92,7 @@ handle query_nowhitespace => sub {
} }
$tmp_expr =~ s#log[_]?(\d{1,3})#(1/log($1))*log#xg; # Arbitrary base logs. $tmp_expr =~ s#log[_]?(\d{1,3})#(1/log($1))*log#xg; # Arbitrary base logs.
$tmp_expr =~ s/ (\d+?)E(-?\d+)([^\d]|\b) /\($1 * 10**$2\)$3/xg; # E == *10^n $tmp_expr =~ s/ (\d+?)E(-?\d+)([^\d]|\b) /\($1 * 10**$2\)$3/ixg; # E == *10^n
$tmp_expr =~ s/\$//g; # Remove $s. $tmp_expr =~ s/\$//g; # Remove $s.
$tmp_expr =~ s/=$//; # Drop =. $tmp_expr =~ s/=$//; # Drop =.
$tmp_expr =~ s/([0-9])\s*([a-zA-Z])([^0-9])/$1*$2$3/g; # Support 0.5e or 0.5pi; but don't break 1e8 $tmp_expr =~ s/([0-9])\s*([a-zA-Z])([^0-9])/$1*$2$3/g; # Support 0.5e or 0.5pi; but don't break 1e8
@ -114,6 +108,7 @@ handle query_nowhitespace => sub {
return unless $style; return unless $style;
$tmp_expr = $style->for_computation($tmp_expr); $tmp_expr = $style->for_computation($tmp_expr);
$tmp_expr =~ s/(Math::BigInt->new\((.*)\))/(Math::BigInt->new\($2\))->bfac()/g; #correct expression for fact
# Using functions makes us want answers with more precision than our inputs indicate. # Using functions makes us want answers with more precision than our inputs indicate.
my $precision = ($query =~ $funcy) ? undef : ($query =~ /^\$/) ? 2 : max(map { $style->precision_of($_) } @numbers); my $precision = ($query =~ $funcy) ? undef : ($query =~ /^\$/) ? 2 : max(map { $style->precision_of($_) } @numbers);
my $tmp_result; my $tmp_result;
@ -152,6 +147,7 @@ sub prepare_for_display {
# Show them how 'E' was interpreted. This should use the number styler, too. # Show them how 'E' was interpreted. This should use the number styler, too.
$query =~ s/((?:\d+?|\s))E(-?\d+)/\($1 * 10^$2\)/i; $query =~ s/((?:\d+?|\s))E(-?\d+)/\($1 * 10^$2\)/i;
$query =~ s/\s*\*{2}\s*/^/g; # Use prettier exponentiation. $query =~ s/\s*\*{2}\s*/^/g; # Use prettier exponentiation.
$query =~ s/(Math::BigInt->new\((.*)\))/fact\($2\)/g; #replace Math::BigInt->new( with fact(
$result = $style->for_display($result); $result = $style->for_display($result);
foreach my $name (keys %named_constants) { foreach my $name (keys %named_constants) {
$query =~ s#\($name\)#$name#xig; $query =~ s#\($name\)#$name#xig;
@ -173,7 +169,7 @@ sub spacing {
my ($text, $space_for_parse) = @_; my ($text, $space_for_parse) = @_;
$text =~ s/\s{2,}/ /g; $text =~ s/\s{2,}/ /g;
$text =~ s/(\s*(?<!<)(?:[\+\-\^xX×∙⋅\*\/÷\%]|times|plus|minus|dividedby)+\s*)/ $1 /ig; $text =~ s/(\s*(?<!<)(?:[\+\^xX×∙⋅\*\/÷\%]|(?<!\de)\-|times|plus|minus|dividedby)+\s*)/ $1 /ig;
$text =~ s/\s*dividedby\s*/ divided by /ig; $text =~ s/\s*dividedby\s*/ divided by /ig;
$text =~ s/(\d+?)((?:dozen|pi|gross|squared|score))/$1 $2/ig; $text =~ s/(\d+?)((?:dozen|pi|gross|squared|score))/$1 $2/ig;
$text =~ s/([\(\)])/ $1 /g if ($space_for_parse); $text =~ s/([\(\)])/ $1 /g if ($space_for_parse);

View File

@ -13,16 +13,6 @@ use YAML::XS 'LoadFile';
zci answer_type => "calendar_conversion"; zci answer_type => "calendar_conversion";
zci is_cached => 0; zci is_cached => 0;
primary_example_queries '22/8/2003 to the hijri calendar';
secondary_example_queries '23/6/1424 hijri to gregorian';
description 'convert dates from the Gregorian calendar to the Hijri/Jalali calendars and back';
name 'CalendarConversion';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CalendarConversion.pm';
category 'dates';
topics 'special_interest';
attribution github => ['http://github.com/mattlehning', 'mattlehning'],
github => ['http://github.com/ehsan', 'ehsan'];
triggers any => 'hijri', 'gregorian', 'jalali'; triggers any => 'hijri', 'gregorian', 'jalali';
my $calendars = LoadFile(share('calendars.yml')); my $calendars = LoadFile(share('calendars.yml'));
@ -74,11 +64,15 @@ handle query_lc => sub {
my $converted_date = format_date($od, $om, $oy, $output_calendar); my $converted_date = format_date($od, $om, $oy, $output_calendar);
return $input_date . ' is ' . $converted_date, return $input_date . ' is ' . $converted_date,
structured_answer => { structured_answer => {
input => [$input_date], data => {
operation => 'Calendar conversion', title => $converted_date,
result => $converted_date subtitle => 'Calendar conversion: ' . $input_date
}; },
templates => {
group => 'text'
}
};
}; };
sub g2j { sub g2j {

View File

@ -12,20 +12,6 @@ with 'DDG::GoodieRole::Dates';
zci answer_type => 'calendar'; zci answer_type => 'calendar';
zci is_cached => 0; zci is_cached => 0;
primary_example_queries "calendar";
secondary_example_queries "calendar november",
"calendar next november",
"calendar november 2015",
"cal 29 nov 1980",
"cal 29.11.1980",
"cal 1980-11-29";
description "Print calendar of current / given month and highlight (to)day";
name "Calendar Today";
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CalendarToday.pm';
category "dates";
topics "everyday";
attribution email => ['webmaster@quovadit.org', 'webmaster@quovadit.org'];
triggers startend => 'calendar', 'cal'; triggers startend => 'calendar', 'cal';
# define variables # define variables
@ -84,20 +70,20 @@ sub format_result {
# Print heading # Print heading
my $rText = "\n"; my $rText = "\n";
my $rHtml = '<table class="calendar"><tr><th colspan="7"><span class="circle t_left"><a href="/?q=' . encodeURIComponent('calendar ' . $previous->strftime("%B %Y")) . '"><span class="ddgsi ddgsi-arrow-left"></span></a></span><span class="calendar__header"><b>';
$rHtml .= $firstDay->strftime("%B %Y").'</b></span><span class="circle t_right"><a href="/?q=' . encodeURIComponent('calendar ' . $next->strftime("%B %Y")) . '"><span class="ddgsi ddgsi-arrow-right"></span></a></span></th>';
$rHtml .= '</tr><tr>';
for my $dayHeading (@weekDays) { for my $dayHeading (@weekDays) {
$rText .= $dayHeading . ' '; $rText .= $dayHeading . ' ';
$rHtml .= '<th>' . $dayHeading . '</th>';
} }
$rText .= " ".$firstDay->strftime("%B %Y")."\n"; $rText .= " ".$firstDay->strftime("%B %Y")."\n";
$rHtml .= "</tr><tr>";
# Skip to the first day of the week # Skip to the first day of the week
$rText .= " " x $first_day_num; $rText .= " " x $first_day_num;
$rHtml .= "<td>&nbsp;</td>" x $first_day_num;
my @weeks;
my @week_day;
for (my $t = 1; $t <= $first_day_num; $t++) {
push @week_day, {"day", " ", "today", ""};
}
my $weekDayNum = $first_day_num; my $weekDayNum = $first_day_num;
# Printing the month # Printing the month
@ -105,25 +91,41 @@ sub format_result {
my $padded_date = sprintf('%2s', $dayNum); my $padded_date = sprintf('%2s', $dayNum);
if ($dayNum == $highlightDay) { if ($dayNum == $highlightDay) {
$rText .= '|' . $padded_date . '|'; $rText .= '|' . $padded_date . '|';
$rHtml .= '<td><span class="calendar__today circle">' . $dayNum . '</span></td>'; push @week_day, {"day", $dayNum, "today", "1"};
} else { } else {
$rText .= ' ' . $padded_date . ' '; $rText .= ' ' . $padded_date . ' ';
$rHtml .= "<td>$dayNum</td>"; push @week_day, {"day", $dayNum, "today", ""};
} }
# next row after 7 cells # next row after 7 cells
$weekDayNum++; $weekDayNum++;
if ($weekDayNum == 7) { if ($weekDayNum == 7) {
$weekDayNum = 0; push @weeks, [@week_day];
$rText .= "\n"; $weekDayNum = 0;
$rHtml .= "</tr><tr>"; undef @week_day;
$rText .= "\n";
} }
} }
if (@week_day) {
push @weeks, [@week_day];
}
$rText .= "\n"; $rText .= "\n";
$rHtml .="</tr></table>";
return $rText, html => $rHtml; return $rText,
} structured_answer => {
data => {
month_year => $firstDay->strftime("%B %Y"),
previous_month => $previous->strftime("%B %Y"),
next_month => $next->strftime("%B %Y"),
weeks => \@weeks,
},
templates => {
group => 'text',
item => 0,
options => {
content => 'DDH.calendar_today.content'
}
}
};
};
1; 1;

View File

@ -9,19 +9,6 @@ use Telephony::CountryDialingCodes;
zci answer_type => "calling_codes"; zci answer_type => "calling_codes";
zci is_cached => 1; zci is_cached => 1;
name "CallingCodes";
description "Matches country names to international calling codes";
source "https://en.wikipedia.org/wiki/List_of_country_calling_codes#Alphabetical_listing_by_country_or_region";
code_url "https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Goodie/CallingCodes.pm";
category "geography";
topics "travel", "geography";
primary_example_queries "calling code 55", "dialing code brazil";
secondary_example_queries "dialing code +55", "country calling code 55";
attribution github => ["kablamo", "Eric Johnson"],
web => ["http://kablamo.org", "Eric Johnson"];
my @codewords = qw(code codes); my @codewords = qw(code codes);
my @descriptors = ('calling', 'dialing', 'dial-in', 'dial in'); my @descriptors = ('calling', 'dialing', 'dial-in', 'dial in');
my @extras = qw(international country); my @extras = qw(international country);
@ -59,11 +46,15 @@ handle remainder => sub {
my $country_list = list2string(@countries); my $country_list = list2string(@countries);
return $dialing_code . ' is the international calling code for ' . $country_list . '.', return $dialing_code . ' is the international calling code for ' . $country_list . '.',
structured_answer => { structured_answer => {
input => [$in_number ? $dialing_code : $country_list], data => {
operation => 'International calling code', title => $in_number ? $country_list : $dialing_code,
result => ($in_number ? $country_list : $dialing_code), subtitle => 'International calling code: ' . ($in_number ? $dialing_code : $country_list)
}; },
templates => {
group => 'text'
}
};
}; };
# Convert a list of country names to a single human readable English string. # Convert a list of country names to a single human readable English string.

View File

@ -7,14 +7,6 @@ use strict;
zci answer_type => "camel_case"; zci answer_type => "camel_case";
zci is_cached => 1; zci is_cached => 1;
name "CamelCase";
description "Converts the query to camelCase";
primary_example_queries "camelcase this is a test", "camel case this is another test";
category "programming";
topics "geek", "programming";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CamelCase.pm";
attribution github => ["https://github.com/Sloff", "Sloff"];
# Triggers # Triggers
triggers start => "camelcase", "camel case"; triggers start => "camelcase", "camel case";
@ -35,8 +27,6 @@ handle remainder => sub {
); );
return $camelCase, structured_answer => { return $camelCase, structured_answer => {
id => 'camel_case',
name => 'answer',
data => { data => {
title => $camelCase, title => $camelCase,
subtitle => 'camelCase' subtitle => 'camelCase'

View File

@ -4,15 +4,6 @@ package DDG::Goodie::CanadaPost;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
primary_example_queries 'canada post 123456789';
description 'Track a package from Canada Post';
name 'canadapost';
icon_url "/i/www.canadapost.ca.ico";
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CanadaPost.pm';
category 'ids';
topics 'special_interest';
attribution github => [ 'https://github.com/duckduckgo', 'duckduckgo'];
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "canadapost"; zci answer_type => "canadapost";

View File

@ -21,13 +21,6 @@ triggers startend =>
zci answer_type => "chars"; zci answer_type => "chars";
zci is_cached => 1; zci is_cached => 1;
name 'Character Counter';
description 'Count the number of charaters in a query';
primary_example_queries 'chars in "my string"';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Chars.pm';
category 'computing_tools';
topics 'programming';
handle remainder => sub { handle remainder => sub {
my ($str) = @_; my ($str) = @_;
return if !$str; return if !$str;
@ -54,14 +47,15 @@ handle remainder => sub {
# note that this works for length=0, i.e. we'll correctly get '0 characters'. # note that this works for length=0, i.e. we'll correctly get '0 characters'.
my $characters_pluralized = ($len == 1 ? 'character' : 'characters'); my $characters_pluralized = ($len == 1 ? 'character' : 'characters');
# build the output string return qq("$str" is $len $characters_pluralized long.),
my $text_out = qq("$str" is $len $characters_pluralized long.);
return $text_out,
structured_answer => { structured_answer => {
input => [html_enc($str)], data => {
operation => 'Character count', title => $len,
result => $len subtitle => 'Character count: ' . html_enc($str)
},
templates => {
group => 'text'
}
}; };
}; };

View File

@ -1,33 +1,89 @@
package DDG::Goodie::CheatSheets; package DDG::Goodie::CheatSheets;
# ABSTRACT: Load basic cheat sheets from JSON files # ABSTRACT: Load basic cheat sheets from JSON files
use JSON::XS; use JSON::MaybeXS;
use DDG::Goodie; use DDG::Goodie;
use DDP; use DDP;
use File::Find::Rule; use File::Find::Rule;
use YAML::XS qw(LoadFile);
no warnings 'uninitialized'; no warnings 'uninitialized';
zci answer_type => 'cheat_sheet'; zci answer_type => 'cheat_sheet';
zci is_cached => 1; zci is_cached => 1;
triggers startend => ( my $trigger_data = LoadFile(share('triggers.yaml'));
'char', 'chars',
'character', 'characters',
'cheat sheet', 'cheatsheet',
'command', 'commands',
'example', 'examples',
'guide', 'help',
'quick reference', 'reference',
'shortcut', 'shortcuts',
'symbol', 'symbols',
'key bindings', 'keys', 'default keys'
);
sub getAliases { # Instantiate triggers as defined in 'triggers.json', return a
# hash that allows category and/or cheat sheet look-up based on
# trigger.
sub generate_triggers {
my $aliases = @_;
# Initialize categories
my %categories;
my $category_map = $trigger_data->{template_map};
my %spec_triggers = %{$trigger_data->{categories}};
# Initialize custom triggers
while (my ($id, $spec) = each %{$trigger_data->{custom_triggers} || {}}) {
$category_map->{$id} = $spec->{additional_categories}
if defined $spec->{additional_categories};
$spec_triggers{$id} = $spec->{triggers}
if defined $spec->{triggers};
}
while (my ($template_type, $categories) = each %$category_map) {
foreach my $category (@{$categories}) {
$categories{$category}{$template_type} = 1;
}
}
# Initialize triggers
# This will contain the actual triggers, with the triggers as values and
# the trigger positions as keys (e.g., 'startend' => ['foo'])
my %triggers;
# This will contain a lookup from triggers to categories and/or files.
my %trigger_lookup;
# This will contain all the ignored phrases and their associated categories.
my %ignore_phrases;
while (my ($name, $trigger_setsh) = each %spec_triggers) {
my $ignore_phrases = delete $trigger_setsh->{ignore};
while (my ($trigger_type, $triggersh) = each %$trigger_setsh) {
foreach my $trigger (@{$triggersh}) {
# Add trigger to global triggers.
$triggers{$trigger_type}{$trigger} = 1;
# Handle ignored components - these will be stripped
# from query and not be included in final trigger.
if (defined $ignore_phrases) {
my %new_ignore_phrases = map { $_ => 1 }
(keys %{$ignore_phrases{$trigger} || {}},
@$ignore_phrases);
$ignore_phrases{$trigger} = \%new_ignore_phrases;
}
my %new_triggers = map { $_ => 1}
(keys %{$trigger_lookup{$trigger}});
if ($name !~ /cheat_sheet$/) {
%new_triggers = (%new_triggers, %{$categories{$name} || {}});
} else {
$new_triggers{$name} = 1;
}
$trigger_lookup{$trigger} = \%new_triggers;
}
}
}
while (my ($trigger_type, $triggers) = each %triggers) {
triggers $trigger_type => (keys %{$triggers});
}
return (\%ignore_phrases, %trigger_lookup);
}
# Initialize aliases.
sub get_aliases {
my @files = File::Find::Rule->file() my @files = File::Find::Rule->file()
->name("*.json") ->name("*.json")
->in(share()); ->in(share('json'));
my %results; my %results;
foreach my $file (@files) { foreach my $file (@files) {
@ -46,34 +102,54 @@ sub getAliases {
if ($data->{'aliases'}) { if ($data->{'aliases'}) {
foreach my $alias (@{$data->{'aliases'}}) { foreach my $alias (@{$data->{'aliases'}}) {
$results{lc($alias)} = $file; $results{lc $alias} = $file;
} }
} }
} }
return \%results; return \%results;
} }
my $aliases = getAliases(); my $aliases = get_aliases();
handle remainder => sub { my ($trigger_ignore, %trigger_lookup) = generate_triggers($aliases);
# If needed we could jump through a few more hoops to check
# terms against file names.
open my $fh, $aliases->{join(' ', split /\s+/o, lc($_))} or return;
my $json = do { local $/; <$fh> }; my %ignore_re = map {
my $data = decode_json($json); my $i = join '|', sort { length $b <=> length $a }
keys %{$trigger_ignore->{$_}};
$_ => qr/\b(?:$i)\b/;
} (keys %{$trigger_ignore});
handle remainder_lc => sub {
my $remainder = join ' ', split /\s+/o, shift;
my $trigger = join(' ', split /\s+/o, lc($req->matched_trigger));
my $lookup = $trigger_lookup{$trigger};
my $alias = exists $ignore_re{$trigger}
? ($remainder
=~ s/$ignore_re{$trigger}//gr
=~ s/^\s+//ro
=~ s/\s+$//ro)
: $remainder;
my $file = $aliases->{$alias} or return;
open my $fh, $file or return;
my $json = do { local $/; <$fh> };
my $data = decode_json($json) or return;
# Either the template type of the cheat sheet must support
# the trigger category, or the lookup must explicitly allow
# the current id.
return unless $lookup->{$data->{template_type}} || $lookup->{$data->{id}};
return 'Cheat Sheet', structured_answer => { return 'Cheat Sheet', structured_answer => {
id => 'cheat_sheets', id => 'cheat_sheets',
dynamic_id => $data->{id}, dynamic_id => $data->{id},
name => 'Cheat Sheet', data => $data,
data => $data, templates => {
templates => { group => 'base',
group => 'base', item => 0,
item => 0,
options => { options => {
content => "DDH.cheat_sheets.detail", content => "DDH.cheat_sheets.detail",
moreAt => 0 moreAt => 0
} }
} }
}; };

View File

@ -3,20 +3,11 @@ package DDG::Goodie::Chess960;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
with 'DDG::GoodieRole::Chess';
triggers any => 'random', 'chess960'; triggers any => 'random', 'chess960';
zci is_cached => 0; zci is_cached => 0;
zci answer_type => 'chess960_position'; zci answer_type => 'chess960_position';
primary_example_queries 'chess960 random';
description 'Generates a random starting position for Chess960';
topics 'gaming', 'entertainment';
category 'random';
attribution github => 'https://github.com/koosha--',
twitter => 'https://twitter.com/_koosha_';
my @all_positions = qw( my @all_positions = qw(
BBQNNRKR BQNBNRKR BQNNRBKR BQNNRKRB QBBNNRKR QNBBNRKR QNBNRBKR QNBNRKRB QBNNBRKR QNNBBRKR BBQNNRKR BQNBNRKR BQNNRBKR BQNNRKRB QBBNNRKR QNBBNRKR QNBNRBKR QNBNRKRB QBNNBRKR QNNBBRKR
QNNRBBKR QNNRBKRB QBNNRKBR QNNBRKBR QNNRKBBR QNNRKRBB BBNQNRKR BNQBNRKR BNQNRBKR BNQNRKRB QNNRBBKR QNNRBKRB QBNNRKBR QNNBRKBR QNNRKBBR QNNRKRBB BBNQNRKR BNQBNRKR BNQNRBKR BNQNRKRB
@ -116,13 +107,6 @@ RBKRNQBN RKRBNQBN RKRNQBBN RKRNQNBB BBRKRNNQ BRKBRNNQ BRKRNBNQ BRKRNNQB RBBKRNNQ
RKBRNBNQ RKBRNNQB RBKRBNNQ RKRBBNNQ RKRNBBNQ RKRNBNQB RBKRNNBQ RKRBNNBQ RKRNNBBQ RKRNNQBB RKBRNBNQ RKBRNNQB RBKRBNNQ RKRBBNNQ RKRNBBNQ RKRNBNQB RBKRNNBQ RKRBNNBQ RKRNNBBQ RKRNNQBB
); );
sub make_fen {
my ($position) = @_;
my ($position_lc) = lc$position;
my ($fen) = "${position_lc}/pppppppp/8/8/8/8/PPPPPPPP/${position}";
return parse_position($fen);
}
handle query => sub { handle query => sub {
# Ensure rand is seeded for each process # Ensure rand is seeded for each process
srand(); srand();
@ -136,13 +120,27 @@ handle query => sub {
my $position = $all_positions[$index]; my $position = $all_positions[$index];
my @fen = make_fen($position);
my $position_num = $index + 1; my $position_num = $index + 1;
my $html = draw_chessboard_html(@fen);
$query =~ s/^ chess960|chess960 $|chess960 //i; $query =~ s/^ chess960|chess960 $|chess960 //i;
return draw_chessboard_ascii(@fen), html => $html, heading => "Position $position_num (Chess960)";
return 'Chess 960',
structured_answer => {
data => {
title => 'Chess960',
subtitle => 'Position ' . $position_num,
rows => 8,
columns => 8,
position => $position
},
templates => {
group => 'text',
item => 0,
options => {
content => 'DDH.chess960.content'
}
}
};
}; };
1; 1;

View File

@ -11,16 +11,6 @@ use utf8;
triggers any => 'chinese zodiac', 'shēngxiào', 'shengxiao', 'shēng xiào', 'sheng xiao'; triggers any => 'chinese zodiac', 'shēngxiào', 'shengxiao', 'shēng xiào', 'sheng xiao';
zci is_cached => 0; zci is_cached => 0;
name 'Chinese Zodiac';
description 'Return the Chinese zodiac animal for a given year';
primary_example_queries 'chinese zodiac for 1969';
secondary_example_queries '2004 chinese zodiac animal', 'what was the chinese zodiac animal in 1992', 'what will the chinese zodiac animal be for 2056', 'last year\'s chinese zodiac';
category 'dates';
topics 'special_interest';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ChineseZodiac.pm';
attribution github => ['http://github.com/wilkox', 'wilkox'],
github => ['https://github.com/Sloff', 'Sloff'];
my %animal_to_language = ( my %animal_to_language = (
'hare' => { en => 'Rabbit', zh => '兔' }, 'hare' => { en => 'Rabbit', zh => '兔' },
'dragon' => { en => 'Dragon', zh => '龙' }, 'dragon' => { en => 'Dragon', zh => '龙' },
@ -92,8 +82,6 @@ sub format_answer {
my ($character, $english, $statement) = @_; my ($character, $english, $statement) = @_;
return "$character ($english)", structured_answer => { return "$character ($english)", structured_answer => {
id => "chinese_zodiac",
name => "Chinese Zodiac",
data => { data => {
title => "$character ($english)", title => "$character ($english)",
subtitle => $statement subtitle => $statement

View File

@ -0,0 +1,82 @@
package DDG::Goodie::CoffeeToWaterRatio;
# ABSTRACT: Either return a default drip coffee to water ratio (no weight entered)
# or return a volume of liquid proportional to the coffee weight entered
use DDG::Goodie;
use Math::Round;
with 'DDG::GoodieRole::NumberStyler';
zci answer_type => "coffee_to_water_ratio";
zci is_cached => 1;
triggers startend => "coffee to water", "coffee to water ratio";
my $imperial_fluid_units = 'fl. oz.';
my $metric_fluid_units = 'ml';
my $metric_precision = 1;
my $imperial_precision = 0.1;
my $imperial_to_water = 16;
my $metric_to_water = 16.6945;
my $MAX_WEIGHT = 100000; # limit to 100,000
my %wt = (
'oz' => {
'fluid_units' => $imperial_fluid_units,
'precision' => $imperial_precision,
'ratio' => $imperial_to_water,
},
'g' => {
'fluid_units' => $metric_fluid_units,
'precision' => $metric_precision,
'ratio' => $metric_to_water,
}
);
$wt{ounce} = $wt{oz};
$wt{ounces} = $wt{oz};
$wt{gram} = $wt{g};
$wt{grams} = $wt{g};
my $weight_re = number_style_regex();
sub convertResult {
my ($unit, $weight, $weight_display, $water_ratio, $precision, $fluid_units) = @_;
my $conversion = nearest($precision, $weight * $water_ratio);
return $conversion . " " . $fluid_units . " of water", structured_answer => {
input => [$weight_display . $unit],
operation => 'Water calculation for coffee weight',
result => "$conversion $fluid_units of water",
};
}
handle remainder => sub {
return "1 g to 16.7 ml (0.035 oz. to 0.56 fl. oz.)", structured_answer => {
input => [''],
operation => 'Coffee to water ratio per gram (0.035 ounces)',
result => "16.7 ml (0.56 fl. oz.)",
} unless $_;
return unless my ($weight_ns) = $_ =~ qr/($weight_re)/;
my $weight_styler = number_style_for($weight_ns);
my $weight = $weight_styler->for_computation($weight_ns);
my $weight_display = $weight_styler->for_display($weight);
my ($unit) = $_ =~ /(ounce[s]?|gram[s]?|oz|g)/i;
return unless defined $unit and defined $weight;
return unless $weight > 0 && $weight <= $MAX_WEIGHT;
my $lc_unit = lc($unit);
return convertResult($unit, $weight, $weight_display, $wt{$lc_unit}{'ratio'}, $wt{$lc_unit}{'precision'}, $wt{$lc_unit}{'fluid_units'}) if defined $wt{$lc_unit};
return;
};
1;

View File

@ -8,19 +8,9 @@ zci is_cached => 0;
triggers start => 'flip', 'toss', 'coin', 'heads'; triggers start => 'flip', 'toss', 'coin', 'heads';
primary_example_queries 'flip a coin', 'toss a coin';
secondary_example_queries 'flip 4 coins', 'heads or tails';
description 'flip a coin';
name 'Coin Flip';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Coin.pm';
topics 'trivia';
category 'random';
attribution github => [ 'http://github.com/mattlehning', 'mattlehning' ];
handle query_lc => sub { handle query_lc => sub {
my $flips; my $flips;
if ($_ =~ /^(heads or tails[ ]?[\?]?)|((flip|toss) (a )?coin)$/) { if ($_ =~ /^(heads or tails[ ]?[\?]?)|((flip|toss) (a )?coin)|(coin (flip|toss))$/) {
$flips = 1; $flips = 1;
} elsif ($_ =~ /^(?:flip|toss) (\d{0,2}) coins?$/) { } elsif ($_ =~ /^(?:flip|toss) (\d{0,2}) coins?$/) {
$flips = $1; $flips = $1;

View File

@ -51,16 +51,6 @@ triggers query_raw => qr/^
zci is_cached => 1; zci is_cached => 1;
zci answer_type => 'color_code'; zci answer_type => 'color_code';
primary_example_queries 'hex color code for cyan';
secondary_example_queries 'rgb(173,216,230)', 'hsl 194 0.53 0.79', 'cmyk(0.12, 0, 0, 0)', '#00ff00';
description 'get hex, RGB, HSL and CMYB values for a color';
name 'ColorCodes';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ColorCodes.pm';
category 'conversions';
topics 'programming';
attribution cpan => 'CRZEDPSYC',
github => ['http://github.com/mintsoft', 'Rob Emery'];
my %trigger_invert = map { $_ => 1 } (qw( inverse negative opposite )); my %trigger_invert = map { $_ => 1 } (qw( inverse negative opposite ));
my %trigger_filler = map { $_ => 1 } (qw( code )); my %trigger_filler = map { $_ => 1 } (qw( code ));
@ -72,61 +62,6 @@ sub percentify {
return @out; return @out;
} }
sub create_output {
my (%input) = @_;
my ($text, $html) = ("","");
(my $hex_for_links = $input{'hex'}) =~ s/^#//;
my $hex = "Hex: ".uc($input{'hex'});
my $rgb = "RGBA(" . join(", ", @{$input{'rgb'}}) . ", ".$input{'alpha'}.")";
my $rgb_pct = "RGB(" . join(", ", @{$input{'rgb_percentage'}}) . ")";
my $hsl = "HSL(" . join(", ", @{$input{'hsl'}}) . ")";
my $cmyb = "CMYB(" . join(", ", @{$input{'cmyb'}}) . ")";
my @analogous_colors = @{$input{'analogous'}};
my $complementary = uc $input{'complementary'};
#greyscale colours have no hue and saturation
my $show_column_2 = !($input{'hsl'}->[0] eq 0 && $input{'hsl'}->[1] eq '0%');
$text = "$hex ~ $rgb ~ $rgb_pct ~ $hsl ~ $cmyb";
my $column_2_text = "\n" .
"Complementary: #$complementary\n" .
"Analogous: ".(join ", ", map { "#".uc $_ } @analogous_colors);
my $comps = "<div class='cols_column'>"
. "<a href='/?q=color%20picker%20%23$complementary' class='mini-color circle' style='background: #$complementary'>"
. "</a></div>"
. "<div class='desc_column'><p class='no_vspace'>Complementary #:</p><p class='no_vspace'>"
. qq[<a onclick='document.x.q.value="#$complementary";document.x.q.focus();' href='javascript:' class='tx-clr--lt'>$complementary</a>]
. "</p></div>";
my $analogs = "<div class='cols_column'>"
. (join "", map { "<a href='/?q=color%20picker%20%23".$_."' class='mini-color circle' style='background: #" . $_ . "'> </a>"; } @analogous_colors)
. "</div>"
. "<div class='desc_column'><p class='no_vspace'>Analogous #:</p><p class='no_vspace'>" . (join ", ", map { qq[<a onclick='document.x.q.value="#] .(uc $_). qq[";document.x.q.focus();' href='javascript:' class='tx-clr--lt'>].(uc $_).'</a>' } @analogous_colors) . "</p></div>";
$html = "<div class='column1 tx-clr--dk2'>"
. "<p class='hex tx-clr--dk zci__caption'>$hex</p><p class='no_vspace'>$rgb</p><p class='no_vspace'>$hsl</p><p class='no_vspace'>$cmyb</p>"
. "<p><a href='http://labs.tineye.com/multicolr/#colors=" . $hex_for_links . ";weights=100;' class='tx-clr--dk2'>Images</a>"
. "<span class='separator'> | </span>"
. "<a href='http://www.color-hex.com/color/" . $hex_for_links . "' title='Tints, information and similar colors on color-hex.com' class='tx-clr--dk2'>Info</a>"
. "<span class='separator'> | </span>"
. "<a href='/?q=color%20picker%20%23" . $hex_for_links . "' class='tx-clr--dk2'>Picker</a></p>"
. "</div>";
my $column_2_html = "<div class='column2 tx-clr--dk2'>"
. "<div class='complementary'>$comps</div>"
. "<div>$analogs</div>"
. "</div>";
if ($show_column_2) {
$html.= $column_2_html;
$text.= $column_2_text;
}
return ($text, $html);
}
handle matches => sub { handle matches => sub {
my $color; my $color;
@ -166,7 +101,7 @@ handle matches => sub {
}; };
} }
my $col = try { Convert::Color->new("$type:$color") }; # Everything should be ready for conversion now. my $col = try { Convert::Color->new("$type:$color") }; # Everything should be ready for conversion now.
return unless $col; # Guess not. return unless $col; # Guess not.
if ($inverse) { # We triggered on the inverse, so do the flip. if ($inverse) { # We triggered on the inverse, so do the flip.
@ -178,30 +113,51 @@ handle matches => sub {
my $complementary = $color_mix->complementary($hex_code); my $complementary = $color_mix->complementary($hex_code);
my @analogous = $color_mix->analogous($hex_code,12,12); my @analogous = $color_mix->analogous($hex_code,12,12);
@analogous = ($analogous[1], $analogous[11]); @analogous = (uc($analogous[1]), uc($analogous[11]));
my @rgb = $col->as_rgb8->rgb8; my @rgb = $col->as_rgb8->rgb8;
my $hsl = $col->as_hsl; my $hsl = $col->as_hsl;
my @rgb_pct = percentify($col->as_rgb->rgb); my @rgb_pct = percentify($col->as_rgb->rgb);
my @cmyk = percentify($col->as_cmyk->cmyk); my @cmyk = percentify($col->as_cmyk->cmyk);
my %outdata = (
hex => '#' . $hex_code,
rgb => \@rgb,
rgb_percentage => \@rgb_pct,
hsl => [round($hsl->hue), percentify($hsl->saturation), percentify($hsl->lightness)],
cmyb => \@cmyk,
alpha => $alpha,
complementary => $complementary,
analogous => \@analogous
);
my ($text, $html_text) = create_output(%outdata); my @hsl = (round($hsl->hue), percentify($hsl->saturation), percentify($hsl->lightness));
return $text, my $hexc = 'Hex: #' . uc($hex_code);
html => '<div class="zci--color-codes"><a href="/?q=color%20picker%20%23' . $hex_code . '" ' my $rgb = 'RGBA(' . join(', ', @rgb) . ', ' . $alpha . ')';
. 'class="colorcodesbox circle" style="background:#' . $hex_code . '">' my $hslc = 'HSL(' . join(', ', @hsl) . ')';
. '</a>' my $cmyb = 'CMYB(' . join(', ', @cmyk) . ')';
. $html_text my $rgb_pct = 'RGB(' . join(', ', @rgb_pct) . ')';
. "</div>";
$complementary = uc($complementary);
#greyscale colours have no hue and saturation
my $show_column_2 = !($hsl[0] eq 0 && $hsl[1] eq '0%');
my $column_2 = '';
if ($show_column_2) {
$column_2 = "\n" . "Complementary: #$complementary" . "\n" . "Analogous: #$analogous[0], #$analogous[1]";
}
return "$hexc ~ $rgb ~ $rgb_pct ~ $hslc ~ $cmyb$column_2",
structured_answer => {
data => {
hex_code => $hex_code,
hexc => $hexc,
rgb => $rgb,
hslc => $hslc,
cmyb => $cmyb,
show_column_2 => $show_column_2,
analogous => \@analogous,
complementary => $complementary,
},
templates => {
group => 'text',
item => 0,
options => {
content => 'DDH.color_codes.content'
}
}
};
}; };
1; 1;

22
lib/DDG/Goodie/Combination.pm Normal file → Executable file
View File

@ -8,15 +8,6 @@ with 'DDG::GoodieRole::NumberStyler';
zci answer_type => "combination"; zci answer_type => "combination";
zci is_cached => 1; zci is_cached => 1;
name "Combination";
description "Computes combinations and permutations.";
primary_example_queries "10 choose 3", "25 permute 16";
secondary_example_queries "16 permutation 3";
category "calculations";
topics "math";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Combination.pm";
attribution github => ["richardscollin", "Collin Richards"];
triggers any => "choose", "permute", "permutation", "npr", "ncr"; triggers any => "choose", "permute", "permutation", "npr", "ncr";
my $number_re = number_style_regex(); my $number_re = number_style_regex();
@ -60,11 +51,14 @@ handle query => sub {
my $formatted_result = $style->for_display($result); my $formatted_result = $style->for_display($result);
return $formatted_result, return $formatted_result, structured_answer => {
structured_answer => { data => {
input => [$style->for_display($n) . " $operation " . $style->for_display($k)], title => $formatted_result,
operation => 'Calculate', subtitle => $style->for_display($n) . " $operation " . $style->for_display($k)
result => $formatted_result, },
templates => {
group => 'text'
}
}; };
}; };

View File

@ -1,22 +1,14 @@
package DDG::Goodie::Constants; package DDG::Goodie::Constants;
# ABSTRACT: Various Math and Physics constants. # ABSTRACT: Various Math and Physics constants.
use strict;
use warnings;
use DDG::Goodie; use DDG::Goodie;
use YAML::XS qw( LoadFile ); use YAML::XS qw( LoadFile );
zci answer_type => "constants"; zci answer_type => "constants";
zci is_cached => 1; zci is_cached => 1;
name "Constants";
description "Succinct explanation of what this instant answer does";
primary_example_queries "first example query", "second example query";
secondary_example_queries "optional -- demonstrate any additional triggers";
category "formulas";
topics "math";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Constants.pm";
attribution github => ["Roysten", "Roy van der Vegt"],
github => ["hemanth", "Hemanth.HM"],
twitter => "gnumanth";
my $constants = LoadFile(share("constants.yml")); my $constants = LoadFile(share("constants.yml"));
#loop through constants #loop through constants
@ -44,7 +36,10 @@ handle query_lc => sub {
return $result, structured_answer => { return $result, structured_answer => {
input => [], input => [],
operation => $constant->{'name'}, operation => $constant->{'name'},
result => $result result => $result,
meta => {
signal => 'high'
}
}; };
}; };

View File

@ -6,68 +6,53 @@ use DDG::Goodie;
with 'DDG::GoodieRole::NumberStyler'; with 'DDG::GoodieRole::NumberStyler';
use Math::Round qw/nearest/; use Math::Round qw/nearest/;
use bignum;
use Convert::Pluggable;
use utf8; use utf8;
use YAML::XS 'LoadFile';
name 'Conversions'; use List::Util qw(any);
description 'convert between various units of measurement';
category 'calculations';
topics 'computing', 'math';
primary_example_queries 'convert 5 oz to grams';
secondary_example_queries '5 ounces to g', '0.5 nautical miles in km';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Conversions.pm';
attribution github => 'https://github.com/elohmrow',
github => ['https://github.com/mintsoft', 'Rob Emery'],
email => 'bradley@pvnp.us';
zci answer_type => 'conversions'; zci answer_type => 'conversions';
zci is_cached => 1; zci is_cached => 1;
# build the keys: use bignum;
# unit types available for conversion
my $c = new Convert::Pluggable();
my @types = @{$c->get_units()};
my @types = LoadFile(share('ratios.yml'));
my %unit_to_plural = ();
my @units = (); my @units = ();
my %plural_to_unit = ();
foreach my $type (@types) { foreach my $type (@types) {
push(@units, $type->{'unit'}); push(@units, $type->{'unit'});
push(@units, $type->{'plural'}) unless lc $type->{'unit'} eq lc $type->{'plural'};
push(@units, @{$type->{'aliases'}}); push(@units, @{$type->{'aliases'}});
$unit_to_plural{lc $type->{'unit'}} = $type->{'plural'};
$plural_to_unit{lc $type->{'plural'}} = $type->{'unit'};
} }
# build triggers based on available conversion units: # build triggers based on available conversion units:
triggers any => @units; my @triggers = map { lc $_ } @units;
triggers any => @triggers;
# match longest possible key (some keys are sub-keys of other keys): # match longest possible key (some keys are sub-keys of other keys):
my $keys = join '|', reverse sort { length($a) <=> length($b) } @units; my $keys = join '|', map { quotemeta $_ } reverse sort { length($a) <=> length($b) } @units;
my $question_prefix = qr/(?<prefix>convert|what (?:is|are|does)|how (?:much|many|long) (?:is|are)?|(?:number of))?/; my $question_prefix = qr/(?<prefix>convert|what (?:is|are|does)|how (?:much|many|long) (?:is|are)?|(?:number of)|(?:how to convert))?/;
# guards and matches regex # guards and matches regex
my $factor_re = join('|', ('a', 'an', number_style_regex())); my $factor_re = join('|', ('a', 'an', number_style_regex()));
my $guard = qr/^(?<question>$question_prefix)\s?(?<left_num>$factor_re*)\s?(?<left_unit>$keys)\s(?<connecting_word>in|to|into|(?:in to)|from)?\s?(?<right_num>$factor_re*)\s?(?:of\s)?(?<right_unit>$keys)[\?]?$/i; my $guard = qr/^(?<question>$question_prefix)\s?(?<left_num>$factor_re*)\s?(?<left_unit>$keys)\s(?<connecting_word>in|to|into|(?:in to)|from)?\s?(?<right_num>$factor_re*)\s?(?:of\s)?(?<right_unit>$keys)[\?]?$/i;
# exceptions for pluralized forms: # fix precision and rounding:
my %plural_exceptions = ( my $precision = 3;
'stone' => 'stone', my $nearest = '.' . ('0' x ($precision-1)) . '1';
'foot' => 'feet',
'inch' => 'inches',
'pounds per square inch' => 'pounds per square inch',
'ton of TNT' => 'tons of TNT',
'metric horsepower' => 'metric horsepower',
'horsepower' => 'horsepower',
'electrical horsepower' => 'electrical horsepower',
'pounds force' => 'pounds force',
);
my %singular_exceptions = reverse %plural_exceptions; # For a number represented as XeY, returns 1 + Y
sub magnitude_order {
my %temperature_aliases = ( my $number = shift;
'celsius' => '°C', my $to_check = sprintf("%e", $number);
'fahrenheit' => '°F', $to_check =~ /\d++\.?\d++e\+?(?<mag>-?\d++)/i;
'rankine' => '°R', return 1 + $+{mag};
'kelvin' => 'K', }
); my $maximum_input = 10**100;
handle query_lc => sub { handle query_lc => sub {
# hack around issues with feet and inches for now # hack around issues with feet and inches for now
@ -75,10 +60,10 @@ handle query_lc => sub {
$_ =~ s/'/feet/; $_ =~ s/'/feet/;
# hack support for "degrees" prefix on temperatures # hack support for "degrees" prefix on temperatures
$_ =~ s/ degrees (celsius|fahrenheit)/ $1/; $_ =~ s/ degree[s]? (celsius|fahrenheit|rankine)/ $1/;
# hack - convert "oz" to "fl oz" if "ml" contained in query # hack - convert "oz" to "fl oz" if "ml" contained in query
s/(oz|ounces)/fl oz/ if(/ml/ && not /fl oz/); s/\b(oz|ounces)/fl oz/ if(/(ml|cup[s]?)/ && not /fl oz/);
# guard the query from spurious matches # guard the query from spurious matches
return unless $_ =~ /$guard/; return unless $_ =~ /$guard/;
@ -87,12 +72,6 @@ handle query_lc => sub {
return if ("" ne $+{'left_num'} && "" ne $+{'right_num'}); return if ("" ne $+{'left_num'} && "" ne $+{'right_num'});
my $factor = $+{'left_num'}; my $factor = $+{'left_num'};
# if the query is in the format <unit> in <num> <unit> we need to flip
# also if it's like "how many cm in metre"; the "1" is implicitly metre so also flip
# But if the second unit is plural, assume we want the the implicit one on the first
# It's always ambiguous when they are both countless and plural, so shouldn't be too bad.
# Compare factors of both units to ensure proper order when ambiguous # Compare factors of both units to ensure proper order when ambiguous
# also, check the <connecting_word> of regex for possible user intentions # also, check the <connecting_word> of regex for possible user intentions
my @factor1 = (); # conversion factors, not left_num or right_num values my @factor1 = (); # conversion factors, not left_num or right_num values
@ -123,105 +102,173 @@ handle query_lc => sub {
} }
} }
# if the query is in the format <unit> in <num> <unit> we need to flip
# also if it's like "how many cm in metre"; the "1" is implicitly metre so also flip
# But if the second unit is plural, assume we want the the implicit one on the first
# It's always ambiguous when they are both countless and plural, so shouldn't be too bad.
if ( if (
"" ne $+{'right_num'} "" ne $+{'right_num'}
|| ( "" eq $+{'left_num'} || ( "" eq $+{'left_num'}
&& "" eq $+{'right_num'} && "" eq $+{'right_num'}
&& $+{'question'} !~ qr/convert/i && $+{'question'} !~ qr/convert/i
&& !looks_plural($+{'right_unit'}) && !looks_plural($+{'right_unit'})
&& $+{'connecting_word'} !~ qr/to/i && $+{'connecting_word'} !~ qr/to/i ))
&& $factor1[0] > $factor2[0]))
{ {
$factor = $+{'right_num'}; $factor = $+{'right_num'};
@matches = ($matches[1], $matches[0]); @matches = ($matches[1], $matches[0]);
} }
$factor = 1 if ($factor =~ qr/^(a[n]?)?$/); $factor = 1 if ($factor =~ qr/^(a[n]?)?$/);
# fix precision and rounding:
my $precision = 3;
my $nearest = '.' . ('0' x ($precision-1)) . '1';
my $styler = number_style_for($factor); my $styler = number_style_for($factor);
return unless $styler; return unless $styler;
my $result = $c->convert( { return unless $styler->for_computation($factor) < $maximum_input;
my $result = convert({
'factor' => $styler->for_computation($factor), 'factor' => $styler->for_computation($factor),
'from_unit' => $matches[0], 'from_unit' => $matches[0],
'to_unit' => $matches[1], 'to_unit' => $matches[1],
'precision' => $precision, });
} );
return if !$result->{'result'}; return unless defined $result->{'result'};
my $f_result; my $formatted_result = sprintf("%.${precision}f", $result->{'result'});
# if $result = 1.00000 .. 000n, where n <> 0 then $result != 1 and throws off pluralization, so: # if $result = 1.00000 .. 000n, where n <> 0 then $result != 1 and throws off pluralization, so:
$result->{'result'} = nearest($nearest, $result->{'result'}); $result->{'result'} = nearest($nearest, $result->{'result'});
if ($result->{'result'} == 0 || length($result->{'result'}) > 2*$precision + 1) { if ($result->{'result'} == 0 || magnitude_order($result->{result}) >= 2*$precision + 1) {
if ($result->{'result'} == 0) { # rounding error
# rounding error $result = convert({
$result = $c->convert( { 'factor' => $styler->for_computation($factor),
'factor' => $styler->for_computation($factor), 'from_unit' => $matches[0],
'from_unit' => $matches[0], 'to_unit' => $matches[1],
'to_unit' => $matches[1], });
'precision' => $precision,
} );
}
# We only display it in exponent form if it's above a certain number. # We only display it in exponent form if it's above a certain number.
# We also want to display numbers from 0 to 1 in exponent form. # We also want to display numbers from 0 to 1 in exponent form.
if($result->{'result'} > 1_000_000 || $result->{'result'} < 1) { if($result->{'result'} > 1_000_000 || abs($result->{'result'}) < 1) {
$f_result = (sprintf "%.${precision}g", $result->{'result'}); $formatted_result = (sprintf "%.${precision}g", $result->{'result'});
} else {
$f_result = (sprintf "%.${precision}f", $result->{'result'});
} }
} }
# handle pluralisation of units # handle pluralisation of units
# however temperature is never plural and does require "degrees" to be prepended # however temperature is never plural and does require "degrees" to be prepended
if ($result->{'type_1'} ne 'temperature') { if ($result->{'type'} eq 'temperature') {
$result->{'from_unit'} = ($factor == 1 ? "degree" : "degrees") . " $result->{'from_unit'}" if ($result->{'from_unit'} ne "kelvin");
$result->{'to_unit'} = ($result->{'result'} == 1 ? "degree" : "degrees") . " $result->{'to_unit'}" if ($result->{'to_unit'} ne "kelvin");
} else {
$result->{'from_unit'} = set_unit_pluralisation($result->{'from_unit'}, $factor); $result->{'from_unit'} = set_unit_pluralisation($result->{'from_unit'}, $factor);
$result->{'to_unit'} = set_unit_pluralisation($result->{'to_unit'}, $result->{'result'}); $result->{'to_unit'} = set_unit_pluralisation($result->{'to_unit'}, $result->{'result'});
} else {
$result->{'from_unit'} = $temperature_aliases{$result->{'from_unit'}};
$result->{'to_unit'} = $temperature_aliases{$result->{'to_unit'}};
} }
$result->{'result'} = defined($f_result) ? $f_result : sprintf("%.${precision}f", $result->{'result'}); $result->{'result'} = $formatted_result;
$result->{'result'} =~ s/\.0{$precision}$//; $result->{'result'} =~ s/\.0{$precision}$//;
$result->{'result'} = $styler->for_display($result->{'result'}); $result->{'result'} = $styler->for_display($result->{'result'});
my $computable_factor = $styler->for_computation($factor);
if (magnitude_order($computable_factor) > 2*$precision + 1) {
$factor = sprintf('%g', $computable_factor);
};
$factor = $styler->for_display($factor); $factor = $styler->for_display($factor);
return $factor . " $result->{'from_unit'} = $result->{'result'} $result->{'to_unit'}", return "$factor $result->{'from_unit'} = $result->{'result'} $result->{'to_unit'}",
structured_answer => { structured_answer => {
input => [$styler->with_html($factor) . ' ' . $result->{'from_unit'}], data => {
operation => 'Convert', raw_input => $styler->for_computation($factor),
result => $styler->with_html($result->{'result'}) . ' ' . $result->{'to_unit'}, raw_answer => $styler->for_computation($result->{'result'}),
left_unit => $result->{'from_unit'},
right_unit => $result->{'to_unit'},
markup_input => $styler->with_html($factor),
styled_output => $styler->with_html($result->{'result'}),
physical_quantity => $result->{'type'}
},
templates => {
group => 'text',
options => {
content => 'DDH.conversions.content'
}
}
}; };
}; };
sub set_unit_pluralisation { sub looks_plural {
my ($unit, $count) = @_; my ($input) = @_;
my $proper_unit = $unit; # By default, we'll leave it unchanged. return defined $plural_to_unit{lc $input};
my $already_plural = looks_plural($unit);
if ($count == 1 && $already_plural) {
$proper_unit = $singular_exceptions{$unit} || substr($unit, 0, -1);
} elsif ($count != 1 && !$already_plural) {
$proper_unit = $plural_exceptions{$unit} || $unit . 's';
}
return $proper_unit;
} }
sub looks_plural { sub convert_temperatures {
my $unit = shift; my ($from, $to, $in_temperature) = @_;
my @unit_letters = split //, $unit; my $kelvin;
return exists $singular_exceptions{$unit} || $unit_letters[-1] eq 's'; # Convert to SI (Kelvin)
if ($from =~ /^f(?:ahrenheit)?$/i) { $kelvin = ($in_temperature + 459.67) * 5/9; }
elsif ($from =~ /^c(?:elsius)?$/i) { $kelvin = $in_temperature + 273.15; }
elsif ($from =~ /^k(?:elvin)?$/i) { $kelvin = $in_temperature; }
elsif ($from =~ /^r(?:ankine)?$/i) { $kelvin = $in_temperature * 5/9; }
elsif ($from =~ /^reaumur$/i) { $kelvin = $in_temperature * 5/4 + 273.15 }
else { die; }
my $out_temperature;
# Convert to Target Unit
if ($to =~ /^f(?:ahrenheit)?$/i) { $out_temperature = $kelvin * 9/5 - 459.67; }
elsif ($to =~ /^c(?:elsius)?$/i) { $out_temperature = $kelvin - 273.15; }
elsif ($to =~ /^k(?:elvin)?$/i) { $out_temperature = $kelvin; }
elsif ($to =~ /^r(?:ankine)?$/i) { $out_temperature = $kelvin * 9/5; }
elsif ($to =~ /^reaumur$/i) { $out_temperature = ($kelvin - 273.15) * 4/5; }
else { die; }
return $out_temperature;
}
sub get_matches {
my @input_matches = @_;
my @output_matches = ();
foreach my $match (@input_matches) {
foreach my $type (@types) {
if (lc $match eq $type->{'unit'} || lc $match eq lc $type->{'plural'} || grep { $_ eq lc $match } @{$type->{'aliases'}}) {
push(@output_matches,{
type => $type->{'type'},
factor => $type->{'factor'},
unit => $type->{'unit'},
can_be_negative => $type->{'can_be_negative'} || '0'
});
}
}
}
return if scalar(@output_matches) != 2;
return @output_matches;
}
sub convert {
my ($conversion) = @_;
my @matches = get_matches($conversion->{'from_unit'}, $conversion->{'to_unit'});
return if $conversion->{'factor'} < 0 && !($matches[0]->{'can_be_negative'});
# matches must be of the same type (e.g., can't convert mass to length):
return if ($matches[0]->{'type'} ne $matches[1]->{'type'});
my $result;
# run the conversion:
# temperatures don't have 1:1 conversions, so they get special treatment:
if ($matches[0]->{'type'} eq 'temperature') {
$result = convert_temperatures($matches[0]->{'unit'}, $matches[1]->{'unit'}, $conversion->{'factor'})
}
else {
$result = $conversion->{'factor'} * ($matches[1]->{'factor'} / $matches[0]->{'factor'});
}
return {
"result" => $result,
"from_unit" => $matches[0]->{'unit'},
"to_unit" => $matches[1]->{'unit'},
"type" => $matches[0]->{'type'}
};
}
sub set_unit_pluralisation {
my ($unit, $count) = @_;
$unit = $unit_to_plural{lc $unit} if ($count != 1 && !looks_plural($unit));
return $unit;
} }
1; 1;

51
lib/DDG/Goodie/ConvertLatLon.pm Normal file → Executable file
View File

@ -10,15 +10,6 @@ use Math::Round;
zci is_cached => 1; zci is_cached => 1;
name 'Convert Latitude and Longitude';
description 'Convert between latitudes and longitudes expressed in degrees of arc and decimal';
primary_example_queries '71º 10\' 3" in decimal';
secondary_example_queries '71 degrees 10 minutes 3 seconds east in decimal', '- 16º 30\' 0" - 68º 9\' 0" as decimal';
category 'transformations';
topics 'geography', 'math', 'science';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ConvertLatLon.pm';
attribution github => ['http://github.com/wilkox', 'wilkox'];
triggers any => "convert", "dms", "decimal", "latitude", "longitude", "minutes", "seconds"; triggers any => "convert", "dms", "decimal", "latitude", "longitude", "minutes", "seconds";
#Regexes for latitude/longitude, in either dms or decimal format #Regexes for latitude/longitude, in either dms or decimal format
@ -84,7 +75,13 @@ my %cardinalName = (
handle query_nowhitespace => sub { handle query_nowhitespace => sub {
return unless /$latLonQR/; my $query = $_;
return if $query !~ /(([-]|(minus)|(negative))?
[\d\.]+([º°]|((arc[-]?)?deg(ree)?s?))
([\d\.]+(['`ʹ]|((arc[-]?)?min(ute)?s?)))?
([\d\.]+(["]|['`ʹ]{2}|(arc[-]?)?sec(ond)?s?))?
([NSEW]|(north)|(south)|(east)|(west)|)
(,|)){1,2}(longitude|latitude|convert|)(in|to|as|)(decimal|dms|degreesminutesseconds|)(form|)$/ix;
#Loop over all provided latitudes/longitudes #Loop over all provided latitudes/longitudes
# Not going to try and enforce strict latitude/longitude # Not going to try and enforce strict latitude/longitude
@ -213,9 +210,17 @@ handle query_nowhitespace => sub {
} }
my $answer = join(' ' , @results); my $answer = join(' ' , @results);
my $html = wrap_html(\@queries, \@results, $toFormat); my $result = join(", ", @results);
return $answer, html => $html; return $answer, structured_answer => {
data => {
subtitle => "Convert to $toFormat: " . join(", ", @queries),
title => $result
},
templates => {
group => 'text'
}
};
}; };
#Format a degrees-minutes-seconds expression #Format a degrees-minutes-seconds expression
@ -233,7 +238,7 @@ sub format_dms {
#Otherwise, add a minus sign if negative #Otherwise, add a minus sign if negative
} elsif ($dmsSign == -1) { } elsif ($dmsSign == -1) {
$formatted = '' . $formatted; $formatted = '-' . $formatted;
} }
return $formatted; return $formatted;
@ -251,29 +256,11 @@ sub format_decimal {
if ($cardinal) { if ($cardinal) {
$formatted .= ' ' . uc($cardinal); $formatted .= ' ' . uc($cardinal);
} elsif ($decDegrees / abs($decDegrees) == -1) { } elsif ($decDegrees / abs($decDegrees) == -1) {
$formatted = '' . $formatted; $formatted = '-' . $formatted;
} }
return $formatted; return $formatted;
} }
sub wrap_secondary {
my $secondary = shift;
return "<span class='text--secondary'>" . $secondary . "</span>";
}
sub wrap_html {
my @queries = @{$_[0]};
my @results = @{$_[1]};
my $toFormat = $_[2];
my $queries = join wrap_secondary(', '), html_enc(@queries);
my $results = join wrap_secondary(', '), html_enc(@results);
my $html = "<div class='zci--conversions text--primary'>" . $queries . wrap_secondary(' in ' . $toFormat . ': ') . $results . "</div>";
return $html;
}
1; 1;

View File

@ -1,4 +1,5 @@
package DDG::Goodie::CountryCodes; package DDG::Goodie::CountryCodes;
# ABSTRACT: Matches country names to ISO 3166 codes and vice versa # ABSTRACT: Matches country names to ISO 3166 codes and vice versa
use strict; use strict;
@ -8,19 +9,6 @@ use Locale::Country qw/country2code code2country/;
zci answer_type => "country_codes"; zci answer_type => "country_codes";
zci is_cached => 1; zci is_cached => 1;
name "CountryCodes";
description "Matches country names to ISO 3166 codes";
source "https://en.wikipedia.org/wiki/ISO_3166-1";
code_url "https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Goodie/CountryCodes.pm";
category "geography";
topics "travel", "geography";
primary_example_queries "country code Japan", "iso code for Spain";
secondary_example_queries "Russia two letter country code", "3 letter country code of China";
attribution github => ["killerfish", "Usman Raza"],
twitter => ["f1shie", "Usman Raza"];
triggers any => 'country code', 'iso code', 'iso 3166'; triggers any => 'country code', 'iso code', 'iso 3166';
my %numbers = (two => 2, three => 3); my %numbers = (two => 2, three => 3);
@ -48,9 +36,13 @@ handle remainder => sub {
return 'ISO 3166: '. ucfirst $input .' - '. $answer[0], return 'ISO 3166: '. ucfirst $input .' - '. $answer[0],
structured_answer => { structured_answer => {
input => [ucfirst $input], data => {
operation => 'ISO 3166 Country code', title => $answer[0],
result => ($answer[0]), subtitle => "ISO 3166 Country code: " . ucfirst ($input)
},
templates => {
group => "text"
}
}; };
}; };
@ -65,4 +57,3 @@ sub result {
} }
1; 1;

View File

@ -7,14 +7,6 @@ use DDG::Goodie;
zci answer_type => "cron_cheat"; zci answer_type => "cron_cheat";
zci is_cached => 1; zci is_cached => 1;
name "CrontabCheatSheet";
description "Crontab cheat sheet";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CrontabCheatSheet.pm";
category "cheat_sheets";
topics "computing", "geek", "programming", "sysadmin";
primary_example_queries 'crontab help', 'crontab cheat sheet', 'crontab example';
triggers startend => ( triggers startend => (
'cron cheat sheet', 'cron cheat sheet',
'cron cheatsheet', 'cron cheatsheet',
@ -34,8 +26,6 @@ triggers startend => (
'crontab examples' 'crontab examples'
); );
attribution github => ["nkorth", "Nathan Korth"];
my $HTML = share("crontab_cheat_sheet.html")->slurp(iomode => '<:encoding(UTF-8)'); my $HTML = share("crontab_cheat_sheet.html")->slurp(iomode => '<:encoding(UTF-8)');
my $TEXT = share("crontab_cheat_sheet.txt")->slurp(iomode => '<:encoding(UTF-8)'); my $TEXT = share("crontab_cheat_sheet.txt")->slurp(iomode => '<:encoding(UTF-8)');

View File

@ -4,79 +4,57 @@ package DDG::Goodie::CryptHashCheck;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
# A comprehensive reference for hashing functions from Wikipedia.
use constant MD5HERF => "http://en.wikipedia.org/wiki/MD5";
use constant SHA1HREF => "http://en.wikipedia.org/wiki/SHA-1";
use constant SHA2HREF => "http://en.wikipedia.org/wiki/SHA-2";
use constant SHA3HREF => "http://en.wikipedia.org/wiki/SHA-3";
zci is_cached => 1; zci is_cached => 1;
# Only one trigger enabled. # Only one trigger enabled.
triggers start => "hash"; triggers start => "hash";
primary_example_queries 'hash 624d420035fc9471f6e16766b7132dd6bb34ea62'; my %cryptohash = (
secondary_example_queries 'hash 1f9b59a2390bb77d2c446837d6aeab067f01b05732735f47099047cd7d3e9f85'; 128 => "MD5",
description 'Identifies cryptographic hash function type.'; 160 => "SHA-1",
name 'CryptHashCheck'; 224 => "SHA-2/SHA-3",
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CryptHashCheck.pm'; 256 => "SHA-2/SHA-3",
category 'computing_tools'; 384 => "SHA-2/SHA-3",
topics 'cryptography'; 512 => "SHA-2/SHA-3"
);
attribution github => ['https://github.com/digit4lfa1l', 'digit4lfa1l']; my $wiki = "https://en.wikipedia.org/wiki/";
# Remainder function with links to the Wikipedia resources. # Remainder function with links to the Wikipedia resources.
handle remainder => sub { handle remainder => sub {
my ($md5) = /^[0-9a-f]{32}$/i;
if ($md5){
my $text = sprintf qq(This is a 128 bit MD5 cryptographic hash.);
my $html = sprintf qq(This is a 128 bit <a href="%s">MD5</a> cryptographic hash.),MD5HERF;
return $text, html => $html; my $matches = /^([0-9A-Fa-f]{32,128})$/ or return;
} my $chars = 4 * length($1);
return unless defined $cryptohash{$chars};
my ($sha1) = /^[0-9a-f]{40}$/i; my $text = "This is a $chars bit " . $cryptohash{$chars} . " cryptographic hash.";
if ($sha1){
my $text = sprintf qq(This is a 160 bit SHA-1 cryptographic hash.);
my $html = sprintf qq(This is a 160 bit <a href="%s">SHA-1</a> cryptographic hash.),SHA1HREF;
return $text, html => $html; my $moretext;
} my $linkname = $cryptohash{$chars};
my ($sha224) = /^[0-9a-f]{56}$/i; if ($cryptohash{$chars} eq "SHA-2/SHA-3") {
if ($sha224){ $moretext = [{ text => "SHA-3", href => $wiki . "SHA-3" }];
my $text = sprintf qq(This is a 224 bit SHA-2/SHA-3 cryptographic hash.); $linkname = "SHA-2";
my $html = sprintf qq(This is a 224 bit <a href="%s">SHA-2</a>/<a href="%s">SHA-3</a> cryptographic hash.),SHA2HREF,SHA3HREF; }
return $text, html => $html; return $text,
} structured_answer => {
data => {
title => $text,
},
meta => {
sourceName => "Wikipedia " . $linkname,
sourceUrl => "$wiki" . $linkname
},
templates => {
group => 'text',
options => {
moreAt => 1,
moreText => $moretext
}
}
};
my ($sha256) = /^[0-9a-f]{64}$/i;
if ($sha256){
my $text = sprintf qq(This is a 256 bit SHA-2/SHA-3 cryptographic hash.);
my $html = sprintf qq(This is a 256 bit <a href="%s">SHA-2</a>/<a href="%s">SHA-3</a> cryptographic hash.),SHA2HREF,SHA3HREF;
return $text, html => $html;
}
my ($sha384) = /^[0-9a-f]{96}$/i;
if ($sha384){
my $text = sprintf qq(This is a 384 bit SHA-2/SHA-3 cryptographic hash.);
my $html = sprintf qq(This is a 384 bit <a href="%s">SHA-2</a>/<a href="%s">SHA-3</a> cryptographic hash.),SHA2HREF,SHA3HREF;
return $text, html => $html;
}
my ($sha512) = /^[0-9a-f]{128}$/i;
if ($sha512){
my $text = sprintf qq(This is a 512 bit SHA-2/SHA-3 cryptographic hash.);
my $html = sprintf qq(This is a 512 bit <a href="%s">SHA-2</a>/<a href="%s">SHA-3</a> cryptographic hash.),SHA2HREF,SHA3HREF;
return $text, html => $html;
}
return;
}; };
1; 1;

View File

@ -22,21 +22,11 @@ use utf8;
use DDG::Goodie; use DDG::Goodie;
use Locale::SubCountry; use Locale::SubCountry;
use Text::Trim; use Text::Trim;
use JSON; use JSON::MaybeXS;
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "currency_in"; zci answer_type => "currency_in";
primary_example_queries 'currency in australia';
secondary_example_queries 'currency in AU';
description 'find the official currency of a country';
name 'CurrencyIn';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/CurrencyIn.pm';
category 'facts';
topics 'travel';
attribution github => ['https://github.com/Alchymista', 'Alchymista'],
github => ['https://github.com/ozdemirburak', 'Burak Özdemir'];
triggers any => 'currency', 'currencies'; triggers any => 'currency', 'currencies';
my $data = share('currencies.json')->slurp; my $data = share('currencies.json')->slurp;
@ -81,8 +71,6 @@ handle remainder => sub {
} }
return \%data, structured_answer => { return \%data, structured_answer => {
id => "currency_in",
name => "CurrencyIn",
templates => { templates => {
group => 'list', group => 'list',
options => { options => {

View File

@ -6,16 +6,6 @@ use DDG::Goodie;
use Business::CUSIP; use Business::CUSIP;
use Text::Trim; use Text::Trim;
# metadata
name "CUSIP check";
description "Validates the check digit for a unique stock identifier based on the Committee on Uniform Securities Identification Procedures";
primary_example_queries "cusip 037833100";
secondary_example_queries "cusip check 38259P706", "844741108 cusip check";
category "finance";
topics "economy_and_finance";
code_url "https://github.com/tommytommytommy/zeroclickinfo-goodies/lib/DDG/Goodie/Cusip.pm";
attribution github => ["https://github.com/tommytommytommy", 'tommytommytommy'];
triggers startend => "cusip", "check cusip", "cusip check"; triggers startend => "cusip", "check cusip", "cusip check";
zci answer_type => "cusip"; zci answer_type => "cusip";
@ -36,15 +26,21 @@ handle remainder => sub {
my ($output, $htmlOutput); my ($output, $htmlOutput);
if ($cusip->is_valid) { if ($cusip->is_valid) {
$output = html_enc($_)." is a properly formatted CUSIP number."; $output = "$_ is a properly formatted CUSIP number.";
$htmlOutput = "<div class='zci--cusip text--primary'>".html_enc($_)." is a properly formatted <span class='text--secondary'>CUSIP number.</span></div>";
} else { } else {
$output = html_enc($_)." is not a properly formatted CUSIP number."; $output = "$_ is not a properly formatted CUSIP number.";
$htmlOutput = "<div class='zci--cusip text--primary'>".html_enc($_)." is not a properly formatted <span class='text--secondary'>CUSIP number.</span></div>";
} }
# output results # output results
return $output, html => $htmlOutput; return $output,
structured_answer => {
data => {
title => $output,
},
templates => {
group => 'text',
}
};
}; };
1; 1;

View File

@ -7,16 +7,6 @@ use DDG::Goodie;
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "dhl"; zci answer_type => "dhl";
primary_example_queries 'DHL 123456789';
secondary_example_queries 'tracking 1234567891';
description 'Track a package from DHL';
name 'DHL';
icon_url "/i/www.dhl.com.ico";
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/DHL.pm';
category 'ids';
topics 'special_interest';
attribution github => [ 'https://github.com/duckduckgo', 'duckduckgo'];
# Regex for usps. # Regex for usps.
my $dhl_qr = qr/dhl/io; my $dhl_qr = qr/dhl/io;
my $tracking_qr = qr/package|track(?:ing|)|num(?:ber|)|\#/i; my $tracking_qr = qr/package|track(?:ing|)|num(?:ber|)|\#/i;

View File

@ -4,86 +4,142 @@ package DDG::Goodie::DateMath;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
with 'DDG::GoodieRole::Dates'; with 'DDG::GoodieRole::Dates';
with 'DDG::GoodieRole::NumberStyler';
use DateTime::Duration; use DateTime::Duration;
use Lingua::EN::Numericalize; use Lingua::EN::Numericalize;
triggers any => qw( plus minus + - date day week month year days weeks months years); triggers any => qw(second minute hour day week month year);
triggers any => qw(seconds minutes hours days weeks months years);
triggers any => qw(plus minus + -);
triggers any => qw(date time);
zci is_cached => 1; zci is_cached => 0;
zci answer_type => 'date_math'; zci answer_type => 'date_math';
primary_example_queries 'Jan 1 2012 plus 32 days';
secondary_example_queries '1/1/2012 plus 5 months', 'January first minus ten days', 'in 5 weeks', '2 weeks ago', '1 month from today';
description 'calculate the date with an offset';
name 'DateMath';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/DateMath.pm';
category 'time_sensitive';
topics 'everyday';
attribution github => ['http://github.com/cj01101', 'cj01101'];
sub get_duration {
my ($number, $unit) = @_;
$unit = lc $unit . 's';
my $dur = DateTime::Duration->new(
$unit => $number,
);
}
sub get_action_for {
my $action = shift;
return '+' if $action =~ /^(\+|plus|from|in|add)$/i;
return '-' if $action =~ /^(\-|minus|ago|subtract)$/i;
}
sub is_clock_unit {
my $unit = shift;
return $unit =~ /hour|minute|second/i if defined $unit;
return 0;
}
sub should_use_clock {
my ($unit, $form) = @_;
return 1 if is_clock_unit($unit);
return $form =~ /time/i if defined $form;
return 0;
}
sub format_result {
my ($out_date, $use_clock) = @_;
my $output_date = date_output_string($out_date, $use_clock);
return $output_date;
}
sub format_input {
my ($input_date, $action, $unit, $input_number, $use_clock) = @_;
my $in_date = date_output_string($input_date, $use_clock);
my $out_action = "$action $input_number $unit";
return "$in_date $out_action";
}
my $number_re = number_style_regex();
my $datestring_regex = datestring_regex(); my $datestring_regex = datestring_regex();
my $units = qr/(?<unit>second|minute|hour|day|week|month|year)s?/i;
my $relative_regex = qr/(?<number>$number_re|[a-z\s-]+)\s+$units/i;
my $action_re = qr/(?<action>plus|add|\+|\-|minus|subtract)/i;
my $date_re = qr/(?<date>$datestring_regex)/i;
my $operation_re = qr/$date_re(?:\s+$action_re\s+$relative_regex)?/i;
my $from_re = qr/$relative_regex\s+(?<action>from)\s+$date_re?|(?<action>in)\s+$relative_regex/i;
my $ago_re = qr/$relative_regex\s+(?<action>ago)/i;
my $time_24h = time_24h_regex();
my $time_12h = time_12h_regex();
my $relative_dates = relative_dates_regex();
sub build_result {
my ($result, $formatted) = @_;
return $result, structured_answer => {
meta => {
signal => 'high',
},
data => {
title => "$result",
subtitle => "$formatted",
},
templates => {
group => 'text',
},
};
}
sub get_result_relative {
my ($date, $use_clock) = @_;
return unless $date =~ $relative_dates;
my $parsed_date = parse_datestring_to_date($date);
my $result = format_result $parsed_date, $use_clock or return;
return build_result($result, ucfirst $date);
}
sub calculate_new_date {
my ($compute_number, $unit, $input_date) = @_;
my $dur = get_duration $compute_number, $unit;
return $input_date->clone->add_duration($dur);
}
sub get_result_action {
my ($action, $date, $number, $unit, $use_clock) = @_;
$action = get_action_for $action or return;
my $input_number = str2nbr($number);
my $style = number_style_for($input_number) or return;
my $compute_num = $style->for_computation($input_number);
my $out_num = $style->for_display($input_number);
my $input_date = parse_datestring_to_date(
defined($date) ? $date : "today") or return;
my $compute_number = $action eq '-' ? 0 - $compute_num : $compute_num;
my $out_date = calculate_new_date $compute_number, $unit, $input_date;
$unit .= 's' if abs($compute_number) != 1;
my $result = format_result($out_date, $use_clock);
my $formatted_input = format_input($input_date, $action, $unit, $out_num, $use_clock);
return build_result($result, $formatted_input);
}
handle query_lc => sub { handle query_lc => sub {
my $query = $_; my $query = $_;
my $relative_regex = qr!(?<number>\d+|[a-z\s-]+)\s+(?<unit>(?:day|week|month|year)s?)!; return unless $query =~ /^((what ((is|was|will) the )?)?(?<dort>date|time|day)( (was it|will it be|is it|be))? )?($operation_re|$from_re|$ago_re)[\?.]?$/i;
return unless $query =~ qr!^(?:date\s+)?( my $action = $+{action};
(?<date>$datestring_regex)(?:\s+(?<action>plus|\+|\-|minus)\s+$relative_regex)?| my $date = $+{date};
$relative_regex\s+(?<action>from)\s+(?<date>$datestring_regex)? my $number = $+{number};
)$!x; my $unit = $+{unit};
my $dort = $+{dort};
if (!exists $+{'number'}) { my $specified_time = $query =~ /$time_24h|$time_12h/;
my $out_date = date_output_string(parse_datestring_to_date($+{'date'})); my $use_clock = $specified_time || should_use_clock $unit, $dort;
return $out_date,
structured_answer => {
input => [$+{'date'}],
operation => 'Date math',
result => $out_date
};
}
my $input_date = parse_datestring_to_date($+{date}); return get_result_relative($date, $use_clock) unless defined $number;
my $input_number = str2nbr($+{number}); return get_result_action $action, $date, $number, $unit, $use_clock;
my $unit = $+{unit};
# check/tweak other (non-date) input
my %action_map = (
plus => '+',
'+' => '+',
minus => '-',
'-' => '-',
from => '+'
);
my $action = $action_map{$+{action}} || return;
my $number = $action eq '-' ? 0 - $input_number : $input_number;
$unit =~ s/s$//g;
my ($years, $months, $days, $weeks) = (0, 0, 0, 0);
$years = $number if $unit eq "year";
$months = $number if $unit eq "month";
$days = $number if $unit eq "day";
$days = 7 * $number if $unit eq "week";
my $dur = DateTime::Duration->new(
years => $years,
months => $months,
days => $days
);
$unit .= 's' if $input_number > 1; # plural?
my $out_date = date_output_string($input_date->clone->add_duration($dur));
my $in_date = date_output_string($input_date);
my $out_action = "$action $input_number $unit";
return "$in_date $out_action is $out_date",
structured_answer => {
input => [$in_date . ' ' . $out_action],
operation => 'Date math',
result => $out_date
};
}; };
1; 1;

View File

@ -0,0 +1,64 @@
package DDG::Goodie::DayOfWeek;
# ABSTRACT: Calculates day of the week a given date falls on
use strict;
use DDG::Goodie;
with 'DDG::GoodieRole::Dates';
zci answer_type => 'day_of_week';
zci is_cached => 1;
primary_example_queries 'day of week for June 22 1907';
secondary_example_queries 'day of week for 1/1/2012', 'what day will 15 Jan 2025 be', 'what day was 2015-01-02', '01/13/2015 was what day';
description 'calculate day of the week for a date';
name 'DayOfWeek';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/DayOfWeek.pm';
category 'time_sensitive';
topics 'everyday';
attribution github => ['http://github.com/cngarrison', 'cngarrison'];
my @trigger_words = (
'day of week', 'day of the week',
'what day of week', 'what day of the week',
'what was day of week', 'what was day of the week',
'what was the day of the week',
'what day will',
'what day was',
'will be what day',
'was what day',
);
triggers startend => @trigger_words;
my $datestring_regex = datestring_regex();
# Handle statement
handle remainder => sub {
my $remainder = $_;
return unless $remainder =~ qr/(?<date>$datestring_regex)/x;
my $input_date = parse_datestring_to_date($+{date});
return unless $input_date;
my $out_date = date_output_string($input_date);
my $day_of_week = $input_date->day_name;
my $text = "Day of the Week: $day_of_week";
return $text,
structured_answer => {
id => 'day_of_week',
name => 'Answer',
data => {
title => $day_of_week,
subtitle => "Day of the week for: $out_date",
},
templates => {
group => "text",
moreAt => 0
}
};
};
1;

View File

@ -10,15 +10,6 @@ triggers start => "days between", "days", "daysbetween", "days_between", "number
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "days_between"; zci answer_type => "days_between";
primary_example_queries 'days between 01/31/2000 01/31/2001';
secondary_example_queries 'days between 01/31/2000 01/31/2001 inclusive';
description 'calculate the number of days between two dates';
name 'DaysBetween';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/DaysBetween.pm';
category 'calculations';
topics 'everyday';
attribution github => ['http://github.com/JetFault', 'JetFault'];
my $datestring_regex = datestring_regex(); my $datestring_regex = datestring_regex();
handle remainder => sub { handle remainder => sub {

View File

@ -11,28 +11,8 @@ zci answer_type => 'dessert';
# We add is_cached so that we get different results for the same query. # We add is_cached so that we get different results for the same query.
zci is_cached => 0; zci is_cached => 0;
# Metadata for our instant answer.
primary_example_queries 'a dessert that starts with the letter a';
secondary_example_queries 'dessert that begins with the letter z';
description 'Returns a dessert given a letter.';
name 'Dessert';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Dessert.pm';
category 'food';
topics 'food_and_drink', 'special_interest';
attribution github => ['https://github.com/kennydude', 'kennydude'];
source 'http://food.sulekha.com/dishes/course/desserts/alphabets/a.htm';
triggers start => 'dessert', 'desserts', 'a dessert'; triggers start => 'dessert', 'desserts', 'a dessert';
# Returns HTML version of our dessert.
sub to_html {
my $dessert_name = shift;
my $dessert_link = $dessert_name;
$dessert_link =~ s/\s/+/g;
return "<a href='http://duckduckgo.com/?q=$dessert_link+recipe'>" . $dessert_name . "</a>";
}
# This function picks a dessert from our lists. # This function picks a dessert from our lists.
sub itemify { sub itemify {
# This has our list of desserts. # This has our list of desserts.
@ -41,37 +21,50 @@ sub itemify {
my $i = rand scalar @{$dessert_list}; my $i = rand scalar @{$dessert_list};
my $dessert = $dessert_list->[$i]; my $dessert = $dessert_list->[$i];
return $dessert . $end, html => to_html($dessert) . $end; my $dessert_link = $dessert;
$dessert_link =~ s/\s/+/g;
return $dessert . $end,
structured_answer => {
data => {
title => $dessert,
subtitle => $end,
url => "http://duckduckgo.com/?q=$dessert_link+recipe"
},
templates => {
group => 'info',
}
};
}; };
# This is our list of desserts. # This is our list of desserts.
my %desserts = ( my %desserts = (
a => ['Apple Turnover', 'Apple Almond Pancake', 'Amaretto Pumpkin Pie', 'Apple Ice Cream', 'Almond Banana Pie', 'Apricot Cherry Compote', 'Autumn Maple Cutout Cookies', 'Apricot Glaze'], a => ['Apple Turnover', 'Apple Almond Pancake', 'Amaretto Pumpkin Pie', 'Apple Ice Cream', 'Almond Banana Pie', 'Apricot Cherry Compote', 'Autumn Maple Cutout Cookies', 'Apricot Glaze'],
b => ['Banana Split', 'Banana Fritters', 'Butterscotch Ice Cream', 'Baklava', 'Brazo de Mercedes', 'Black Forest Trifles', 'Brownie Pie', 'Bibingka', 'Button Cookies', 'Buttermilk Pancake'], b => ['Banana Split', 'Banana Fritters', 'Butterscotch Ice Cream', 'Baklava', 'Brazo de Mercedes', 'Black Forest Trifles', 'Brownie Pie', 'Bibingka', 'Button Cookies', 'Buttermilk Pancake'],
c => ['Cherry Pie', 'Cupcake', 'Chocolate Sundae', 'Caramelized Apple Pie', 'Coffee Walnut Cake', 'Chiffon Cake', 'Cream Puffs', 'Cherry Crunch', 'Creme Brulee', 'Chocolate Mousse'], c => ['Cherry Pie', 'Cupcake', 'Chocolate Sundae', 'Caramelized Apple Pie', 'Coffee Walnut Cake', 'Chiffon Cake', 'Cream Puffs', 'Cherry Crunch', 'Creme Brulee', 'Chocolate Mousse'],
d => ['Dark Chocolate Souffle', 'Doughnut', 'Date Pudding', 'Date and Walnut Cookies', 'Double-Vanilla Meringue Cookies'], d => ['Dark Chocolate Souffle', 'Doughnut', 'Date Pudding', 'Date and Walnut Cookies', 'Double-Vanilla Meringue Cookies'],
e => ['Eclair', 'Espresso Cake', 'Easter Bunny Cake', 'Espresso Meringue'], e => ['Eclair', 'Espresso Cake', 'Easter Bunny Cake', 'Espresso Meringue'],
f => ['Froyo', 'Fudge Rolls', 'Fig and Honey Ice Cream', 'Fruit Salad', 'Frozen Mango Yogurt', 'Fuzzy Navel Cake', 'Fruit Bar', 'Fried Ice Cream', 'Fruit Flan', 'Fresh Cream Pineapple'], f => ['Froyo', 'Fudge Rolls', 'Fig and Honey Ice Cream', 'Fruit Salad', 'Frozen Mango Yogurt', 'Fuzzy Navel Cake', 'Fruit Bar', 'Fried Ice Cream', 'Fruit Flan', 'Fresh Cream Pineapple'],
g => ['Gingerbread', 'Graham Cracker Pie', 'Gingery Plum Jam', 'Gingersnaps', 'Grilled Fruit Sundae', 'Gingerbread Biscotti'], g => ['Gingerbread', 'Graham Cracker Pie', 'Gingery Plum Jam', 'Gingersnaps', 'Grilled Fruit Sundae', 'Gingerbread Biscotti'],
h => ['Hazelnut Torte', 'Honey Nut Crunch', 'Heart-Shaped Napoleons', 'Honey Cinnamon Cake'], h => ['Hazelnut Torte', 'Honey Nut Crunch', 'Heart-Shaped Napoleons', 'Honey Cinnamon Cake'],
i => ['Ice Cream Sandwich', 'Irish Cream-Espresso Crème Caramel', 'Italian Chocolate Truffle', 'Italian Cake'], i => ['Ice Cream Sandwich', 'Irish Cream-Espresso Crème Caramel', 'Italian Chocolate Truffle', 'Italian Cake'],
j => ['Jam roly-poly', 'Jelly Cake', 'Jam Crostata', 'Jam-Filled Cookies'], j => ['Jam roly-poly', 'Jelly Cake', 'Jam Crostata', 'Jam-Filled Cookies'],
k => ['Key Lime Pie', 'Kiwi Cheesecake'], k => ['Key Lime Pie', 'Kiwi Cheesecake'],
l => ['Lollipop', 'Liquorice', 'Lychee Sorbet', 'Lemon Fluff Pie', 'Linzer Cookies', 'Lemon Frozen Yogurt', 'Lemon Meringue Pie', 'Low-Fat Cherry Cobbler'], l => ['Lollipop', 'Liquorice', 'Lychee Sorbet', 'Lemon Fluff Pie', 'Linzer Cookies', 'Lemon Frozen Yogurt', 'Lemon Meringue Pie', 'Low-Fat Cherry Cobbler'],
m => ['Macaroons', 'Meringues', 'Marshmallow', 'Morning Glory Muffins', 'Mango Pudding', 'Madeira Cake', 'Mango Fool', 'Mocha Parfait', 'Mint Fudge Tart', 'Marlborough Pie'], m => ['Macaroons', 'Meringues', 'Marshmallow', 'Morning Glory Muffins', 'Mango Pudding', 'Madeira Cake', 'Mango Fool', 'Mocha Parfait', 'Mint Fudge Tart', 'Marlborough Pie'],
n => ['Nougat', 'Nutella', 'Nutty Graham Cake', 'New York Cheesecake', 'Noodle Pudding'], n => ['Nougat', 'Nutella', 'Nutty Graham Cake', 'New York Cheesecake', 'Noodle Pudding'],
o => ['Oatmeal Pie', 'Oreos', 'Orange Muffins', 'Oreo Cookie Cheesecake', 'Orange Chiffon Cake', 'Olympic Gold Medal Cookies'], o => ['Oatmeal Pie', 'Oreos', 'Orange Muffins', 'Oreo Cookie Cheesecake', 'Orange Chiffon Cake', 'Olympic Gold Medal Cookies'],
p => ['Profiteroles', 'Pumpkin Pie', 'Pineapple Cake', 'Pistachio Ice Cream', 'Peanut Butter Cupcakes', 'Plum Pudding', 'Pumpkin Fudge'], p => ['Profiteroles', 'Pumpkin Pie', 'Pineapple Cake', 'Pistachio Ice Cream', 'Peanut Butter Cupcakes', 'Plum Pudding', 'Pumpkin Fudge'],
q => ['Quiche', 'Quinoa Apple Pie', 'Queen of Puddings', 'Quebec Sugar Pie'], q => ['Quiche', 'Quinoa Apple Pie', 'Queen of Puddings', 'Quebec Sugar Pie'],
r => ['Rocky Road','Red Velvet Cake','Rhubarb and Custard', 'Raisin Muffins', 'Raspberry Jam Hearts', 'Rainbow Cake', 'Rice Pudding', 'Rum Cake', 'Rugelach'], r => ['Rocky Road','Red Velvet Cake','Rhubarb and Custard', 'Raisin Muffins', 'Raspberry Jam Hearts', 'Rainbow Cake', 'Rice Pudding', 'Rum Cake', 'Rugelach'],
s => ['Sundae', 'Strudel', 'Strawberries and Cream', 'Soufflé', 'Sponge Cake', 'Strawberry Trifle', 'Sweet Potato Pudding', 'Stuffed Granny Smiths'], s => ['Sundae', 'Strudel', 'Strawberries and Cream', 'Soufflé', 'Sponge Cake', 'Strawberry Trifle', 'Sweet Potato Pudding', 'Stuffed Granny Smiths'],
t => ['Tiramisu', 'Trifle', 'Twinkies', 'Taffy', 'Toffee Bars', 'Tropical Paradise Pancakes', 'Tangerine Creme Brulee', 'Tres Leches Cake', 'Tea Cake'], t => ['Tiramisu', 'Trifle', 'Twinkies', 'Taffy', 'Toffee Bars', 'Tropical Paradise Pancakes', 'Tangerine Creme Brulee', 'Tres Leches Cake', 'Tea Cake'],
u => ['Upside-down cake', 'Unbaked Chocolate Cookies', 'Ultimate Chocolate Cake', 'Upside Down Orange Cake'], u => ['Upside-down cake', 'Unbaked Chocolate Cookies', 'Ultimate Chocolate Cake', 'Upside Down Orange Cake'],
v => ['Vanilla Swirl', 'Valentine Cake', 'Vanilla Poached Peaches', 'Vanilla Pudding', 'Valentine Cupcakes'], v => ['Vanilla Swirl', 'Valentine Cake', 'Vanilla Poached Peaches', 'Vanilla Pudding', 'Valentine Cupcakes'],
w => ['Waffles','Watermelon','White Chocolate', 'Whole Wheat Muffins', 'Walnut Fudge', 'White Chocolate Snowflakes', 'Watermelon Pie'], w => ['Waffles','Watermelon','White Chocolate', 'Whole Wheat Muffins', 'Walnut Fudge', 'White Chocolate Snowflakes', 'Watermelon Pie'],
x => ['Xmas Cake'], x => ['Xmas Cake'],
y => ['Yellow Layer Cake', 'Yogurt Fruit Pops', 'Yogurt Sundae', 'Yogurt Cheesecake'], y => ['Yellow Layer Cake', 'Yogurt Fruit Pops', 'Yogurt Sundae', 'Yogurt Cheesecake'],
z => ['Zeppole', 'Zucchini Cake', 'Zebra-Stripe Cheesecake'], z => ['Zeppole', 'Zucchini Cake', 'Zebra-Stripe Cheesecake'],
); );
# This function checks if the query string matches the beginning of one of the desserts. # This function checks if the query string matches the beginning of one of the desserts.
@ -85,34 +78,33 @@ sub begins_with {
# Check if a value exists given our key. # Check if a value exists given our key.
if(exists $desserts{$letter}) { if(exists $desserts{$letter}) {
my $value = $desserts{$letter}; my $value = $desserts{$letter};
# Check if a certain dessert begins with our query string. # Check if a certain dessert begins with our query string.
for my $dessert (@{$value}) { for my $dessert (@{$value}) {
if($dessert =~ /^$query/i) { if($dessert =~ /^$query/i) {
push @items, $dessert; push @items, $dessert;
} }
} }
} }
return @items; return @items;
} }
handle remainder => sub { handle remainder => sub {
# Ensure rand is seeded for each process # Ensure rand is seeded for each process
srand(); srand();
# Check the query. See if it matches this regular expression. # Check the query. See if it matches this regular expression.
if(lc $_ =~ m/^(?:that )?(?:start|beginn?)s?(?:ing)? (?:with)? (the letter )?([a-z].*)$/i) { if(lc $_ =~ m/^(?:that )?(?:start|beginn?)s?(?:ing)? (?:with)? (the letter )?([a-z].*)$/i) {
# Check which desserts begin with this letter (or word). # Check which desserts begin with this letter (or word).
my @items = begins_with($2); my @items = begins_with($2);
# Check if we found any results. # Check if we found any results.
if(@items > 0) { if(@items > 0) {
return itemify(\@items, " is a dessert that begins with '$2'."); return itemify(\@items, " is a dessert that begins with '$2'.");
} }
} }
return; return;
}; };

View File

@ -10,16 +10,6 @@ zci answer_type => 'dewey_decimal';
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'dewey 644';
secondary_example_queries 'etymology in the dewey decimal system', 'dewey decimal system 640s', 'dewey decimal system naturalism';
description 'get the topic or reference number of a Dewey Decimal class';
name 'Dewey';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Dewey.pm';
category 'reference';
topics 'special_interest';
attribution twitter => 'crazedpsyc',
cpan => 'CRZEDPSYC' ;
my %nums = share('dewey.txt')->slurp; my %nums = share('dewey.txt')->slurp;
my %types = reverse %nums; my %types = reverse %nums;

View File

@ -4,23 +4,13 @@ package DDG::Goodie::Dice;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
use Lingua::EN::Numericalize;
triggers start => "roll", "throw"; triggers start => "roll", "throw";
zci answer_type => "dice_roll"; zci answer_type => "dice_roll";
zci is_cached => 0; zci is_cached => 0;
primary_example_queries 'throw dice';
secondary_example_queries 'roll 5 dice', 'roll 3d12', 'roll 3d12 and 2d4', 'roll 2 dice and 3d5';
description 'give the results of a random die throw';
name 'Dice';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Dice.pm';
category 'random';
topics 'math';
attribution cpan => ['CRZEDPSYC','crazedpsyc'],
twitter => [ 'loganmccamon', 'loganom'],
github => ['loganom', 'loganom'];
my %utf8_dice = ( my %utf8_dice = (
1 => "\x{2680}", 1 => "\x{2680}",
2 => "\x{2681}", 2 => "\x{2681}",
@ -45,6 +35,9 @@ sub set_num_dice {
my $num_dice = $_[0]; my $num_dice = $_[0];
my $num_dice_default = $_[1]; my $num_dice_default = $_[1];
if(defined($num_dice)){ if(defined($num_dice)){
if ($num_dice =~ /^[a-zA-Z\s\-]+$/) {
return str2nbr($num_dice);
}
if ($num_dice ne ''){ if ($num_dice ne ''){
return $num_dice; return $num_dice;
}else{ }else{
@ -66,11 +59,13 @@ sub shorthand_roll_output {
if (@rolls > 1) { # if( sizeOf(rolls) > 1) if (@rolls > 1) { # if( sizeOf(rolls) > 1)
$out = join(' + ', @rolls); # append current roll to output $out = join(' + ', @rolls); # append current roll to output
$out =~ s/\+\s\-/\- /g; # rewrite + -1 as - 1 $out =~ s/\+\s\-/\- /g; # rewrite + -1 as - 1
$out .= " = $sum"; # append sum of rolls to output if ($_[2] > 1) {
$out .= " = $sum"; # append sum of rolls to output
}
} else { } else {
$out = $sum; # output is roll value if we have just one roll $out = $sum; # output is roll value if we have just one roll
} }
return $out . '<br/>'; return $out;
} }
handle remainder_lc => sub { handle remainder_lc => sub {
@ -80,25 +75,34 @@ handle remainder_lc => sub {
my @values = split(' and ', $_); my @values = split(' and ', $_);
my $values = @values; # size of @values; my $values = @values; # size of @values;
my $out = ''; my $out = '';
my $html = ''; my $diceroll;
my @result;
my $heading = "Random Dice Roll"; my $heading = "Random Dice Roll";
my $total; # total of all dice rolls my $total; # total of all dice rolls
foreach (@values) { foreach (@values) {
if ($_ =~ /^(?:a? ?die|(\d{0,2})\s*dic?e)$/) { if ($_ =~ /^(?:a? ?die|(\d{0,2}|[a-zA-Z\s\-]+)\s*dic?es?)$/) {
# ex. 'a die', '2 dice', '5dice' # ex. 'a die', '2 dice', '5dice', 'five dice'
my @output; my @output;
my $sum = 0; my $sum = 0;
my $number_of_dice = set_num_dice($1, 2); # set number of dice, default 2 my $number_of_dice = set_num_dice($1, 2); # set number of dice, default 2
my $number_of_faces = 6; # number of utf8_dice my $number_of_faces = 6; # number of utf8_dice
if ($number_of_dice !~ /^\d+$/) {
return;
}
for (1 .. $number_of_dice) { # for all rolls for (1 .. $number_of_dice) { # for all rolls
my $roll = roll_die( $number_of_faces ); # roll the die my $roll = roll_die( $number_of_faces ); # roll the die
$sum += $roll; # track sum $sum += $roll; # track sum
push @output, $utf8_dice{$roll}; # add our roll to array output push @output, $utf8_dice{$roll}; # add our roll to array output
} }
$total += $sum; # track total of all rolls $total += $sum; # track total of all rolls
$out .= join(', ', @output) . '<br/>'; $out .= join(', ', @output);
$html .= '<span class="zci--dice-die">' . join(' ', @output).'</span>' $diceroll = join(' ', @output);
.'<span class="zci--dice-sum">'." = ". $sum.'</span></br>'; push @result, {
'sum' => $sum,
'rolls' => $diceroll,
'isdice' => 1
};
} }
elsif ($_ =~ /^(\d*)[d|w](\d+)\s?([+-])?\s?(\d+|[lh])?$/) { elsif ($_ =~ /^(\d*)[d|w](\d+)\s?([+-])?\s?(\d+|[lh])?$/) {
# ex. '2d8', '2w6 - l', '3d4 + 4', '3d4-l' # ex. '2d8', '2w6 - l', '3d4 + 4', '3d4-l'
@ -135,28 +139,53 @@ handle remainder_lc => sub {
for (@rolls) { for (@rolls) {
$sum += $_; # track sum $sum += $_; # track sum
} }
my $roll_output = shorthand_roll_output( \@rolls, $sum ); # initialize roll_output my $roll_output = shorthand_roll_output( \@rolls, $sum, $values ); # initialize roll_output
$out .= $roll_output; # add roll_output to our result $out .= $roll_output; # add roll_output to our result
$html .= $roll_output; # add roll_output to our HTML result push @result, {
'sum' => $sum,
'rolls' => [@rolls],
'isdice' => 0
};
$total += $sum; # add the local sum to the total $total += $sum; # add the local sum to the total
}else{ }else{
# an element of @value was not valid # an element of @value was not valid
return; return;
} }
} }
if($values > 1) { my $group = 'text';
my $data = {
title => $total,
list => \@result
};
my $options = {
subtitle_content => 'DDH.dice.subtitle_content'
};
if ($values > 1) {
# display total sum if more than one value was specified # display total sum if more than one value was specified
$out .= 'Total: ' . $total; $out .= 'Total: ' . $total;
$html .= 'Total: ' . $total; $group = 'list';
$options = {
list_content => 'DDH.dice.content'
};
} }
$out =~ s/<br\/>$//g; # remove trailing newline $out =~ s/<br\/>$//g; # remove trailing newline
if($out eq ''){
if ($out eq '') {
return; # nothing to return return; # nothing to return
}else{
return answer => $out,
html => $html,
heading => $heading;
} }
return $out,
structured_answer => {
data => $data,
templates => {
group => $group,
options => $options
}
};
}; };
1; 1;

View File

@ -6,73 +6,86 @@ use DDG::Goodie;
use YAML::XS 'LoadFile'; use YAML::XS 'LoadFile';
primary_example_queries 'duckduckgo help'; my @ddg_aliases = map { ("${_}'s", "${_}s", $_) } ('duck duck go', 'duckduck go', 'duck duckgo', 'duckduckgo', 'ddg');
secondary_example_queries 'ddg tor', 'short URL for duck duck go'; my @any_triggers = (@ddg_aliases, "zeroclickinfo", "private search", "whois");
description 'DuckDuckGo help and quick links';
name 'DuckDuckGo';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/DuckDuckGo.pm';
category 'cheat_sheets';
topics 'everyday';
attribution twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'];
my @ddg_aliases = map { ($_, $_ . "'s", $_ . "s") } ('duck duck go', 'duckduck go', 'duck duckgo', 'duckduckgo', 'ddg'); triggers any => @any_triggers;
triggers any => @ddg_aliases, "zeroclickinfo", "private search";
zci is_cached => 1; zci is_cached => 1;
my $trigger_qr = join('|', map { quotemeta } @any_triggers);
$trigger_qr = qr/\b(?:$trigger_qr)\b/i;
#warn $trigger_qr;
my $responses = LoadFile(share('responses.yml')); my $responses = LoadFile(share('responses.yml'));
# The YAML is intended to be human-friendly. # The YAML is intended to be human-friendly.
# Now we make something computer-friendly. # Now we make something computer-friendly.
foreach my $keyword (keys %$responses) { foreach my $keyword (keys %$responses) {
my $response = $responses->{$keyword}; my $response = $responses->{$keyword};
if (my $base_format = $response->{base_format}) {
# We need to produce the output for each version.
if (my $info_url = $response->{info_url}) {
$response->{text} = $base_format;
$response->{text} =~ s/[\[\]]//g; # No internal linking.
$response->{text} .= ': ' . $response->{info_url}; # Stick the link on the end.
$response->{html} = $base_format; # Setup basic response.
$response->{html} =~ s#\[#<a href='$info_url'>#; # Insert link. if ($response->{title}) {
$response->{html} =~ s#\]#</a>#; $response->{text} = $response->{title};
$response->{html} .= '.'; if ($response->{url}) {
} else { $response->{text} .= ' '.$response->{url};
# No link to insert, so it must be ready for both.
$response->{text} = $response->{html} = $response->{base_format};
} }
} else {
$response->{text} = '';
} }
# If there's no subtitle, make the subtitle the url text.
unless ($response->{subtitle}) {
$response->{subtitle} = $response->{url};
}
# Setup aliases.
if (ref($response->{aliases}) eq 'ARRAY') { if (ref($response->{aliases}) eq 'ARRAY') {
foreach my $alias (@{$response->{aliases}}) { foreach my $alias (@{$response->{aliases}}) {
# Assume we didn't add an alias for an existing keyword. # Assume we didn't add an alias for an existing keyword.
$responses->{$alias} = $response; $responses->{$alias} = $response;
} }
} }
foreach my $key (keys %$response) {
# No matter what they added, we only use the following keys for the actual response.
delete $response->{$key} unless (grep { $key eq $_ } (qw(text html)));
}
} }
my $skip_words_re = qr/\b(?:what|where|is|of|for|the|in|on)\b/; my $skip_words_re = qr/\b(?:what|where|is|of|for|the|in|on)\b/;
handle remainder => sub { handle query_raw => sub {
my $key = lc; my $key = lc;
$key =~ s/\?//g; # Allow for questions, but don't pollute skip words. my ($trigger) = $key =~ /($trigger_qr)/;
$key =~ s/$skip_words_re//g;
$key =~ s/\W+//g; $key =~ s/\b$trigger\b//g; # Strip trigger on word boundaries.
$key =~ s/\?//g; # Allow for questions, but don't pollute skip words.
$key =~ s/$skip_words_re//g; # Strip skip words.
$key =~ s/\W+//g; # Strip all non-word characters.
#warn "Query: '$_'\tTrigger: '$trigger'\tMajor Key: '$key'";
# Whois escape hatch for invalid keys.
if ($trigger eq 'whois') {
return if $key ne 'duckduckgoownedserveryahoonet';
$key = 'yahoo';
}
my $response = $responses->{$key}; my $response = $responses->{$key};
return unless $response;
return unless ($response);
return $response->{text}, return $response->{text},
structured_answer => { structured_answer => {
input => [], data => {
operation => 'DuckDuckGo info', title => $response->{title},
result => $response->{html}}; subtitle_image => $response->{image},
subtitle_text => $response->{subtitle},
subtitle_url => $response->{url}
},
templates => {
group => 'text',
options => {
subtitle_content => 'DDH.duck_duck_go.subtitle_content'
}
}
};
}; };
1; 1;

View File

@ -2,6 +2,7 @@ package DDG::Goodie::EmToPx;
# ABSTRACT: em <-> px for font sizes. # ABSTRACT: em <-> px for font sizes.
use strict; use strict;
use warnings;
use DDG::Goodie; use DDG::Goodie;
triggers any => "em", "px"; triggers any => "em", "px";
@ -9,34 +10,49 @@ triggers any => "em", "px";
zci answer_type => "conversion"; zci answer_type => "conversion";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries '10 px to em'; sub em_to_px {
secondary_example_queries '12.2 px in em assuming a 12.2 font size'; my ($to_convert, $fontsize) = @_;
description 'convert from px to em'; return $to_convert * $fontsize;
name 'EmToPx'; }
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/EmToPx.pm';
category 'conversions';
topics 'programming';
attribution twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'];
handle query_raw => sub { sub px_to_em {
my $q = lc $_; my ($to_convert, $fontsize) = @_;
$q =~ s/(?![\.\s])\W//g; return sprintf('%.3f', $to_convert / $fontsize) =~ s/\.?0+$//r;
return unless $q =~ /^(?:convert|change|what\s*(?:s|is|are)\s+)?(?<num>\d+\.\d*|\d*\.\d+|\d+)\s*(?<source>em|px)\s+(?:in|to)\s+(?<target>em|px)(?:\s+(?:with|at|using|assuming)(?:\s+an?)?\s+(?<fs>\d+\.\d*|\d*\.\d+|\d+)\s*px)?/; }
my ($target, $num, $source, $fontsize) = map { $+{$_} } qw(target num source fs);
$fontsize ||= 16;
my $number = qr/(\d++\.\d*+|\d*+\.\d++|\d++)/;
my $whatis = qr/(convert|change|what\s*('?s|is|are))?/i;
my $amount = qr/(?<amount>$number)/i;
my $source = qr/(?<source>em|px)/i;
my $target = qr/(?<target>em|px)/i;
my $fs = qr/((font[ -]?|base[- ]?pixel[- ]?)size)/i;
my $fontsize = qr/((with|at|using|assuming)(\s*an?)?)?\s*(?<fm>$fs\s*(of)?\s*)?((?<fontsize>$number)\s*(px)?)\s*(?(<fm>)|($fs))?/i;
handle query_lc => sub {
my $query = $_;
$query =~ s/(?![\.\s])\W//g;
return unless $query =~ /^${whatis}?\s*${amount}\s*${source}\s*(?:in|to)\s*${target}\s*${fontsize}?\s*$/i;
my $target = $+{target};
my $num = $+{amount};
my $source = $+{source};
my $fontsize = $+{fontsize} // 16;
return if ($target eq $source || !$num); return if ($target eq $source || !$num);
my $result = ($target eq 'px') ? $num * $fontsize : $num / $fontsize; my $result = ($target eq 'px') ? em_to_px($num, $fontsize) . 'px'
my $plur = $result == 1 ? "is" : "are"; : px_to_em($num, $fontsize) . 'em';
my $formatted_input = "Convert $num $source to $target with a font-size of ${fontsize}px";
return "There $plur $result $target in $num $source (assuming a ${fontsize}px font size)", return $result,
structured_answer => { structured_answer => {
input => [$num . $source, $fontsize . 'px font size'], data => {
operation => 'Convert to ' . $target, title => $result,
result => $result . $target subtitle => $formatted_input,
}; },
templates => {
group => 'text',
moreAt => 0,
},
};
}; };
1; 1;

View File

@ -6,21 +6,11 @@ use DDG::Goodie;
use Net::Domain::TLD; use Net::Domain::TLD;
use Email::Valid; use Email::Valid;
primary_example_queries 'validate foo@example.com';
description 'Checks given email address.';
name 'Email';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/EmailValidator.pm';
topics 'sysadmin';
category 'computing_info';
zci answer_type => 'email_validation'; zci answer_type => 'email_validation';
zci is_cached => 1; zci is_cached => 1;
triggers start => 'validate', 'validate my email', 'validate my e-mail'; triggers start => 'validate', 'validate my email', 'validate my e-mail';
attribution github => ['https://github.com/stelim', 'Stefan Limbacher'],
twitter => ['http://twitter.com/stefanlimbacher', 'Stefan Limbacher'];
my $message_part = { my $message_part = {
tldcheck => 'top-level domain', tldcheck => 'top-level domain',
fqdn => 'fully qualified domain name', fqdn => 'fully qualified domain name',

View File

@ -7,17 +7,6 @@ use DDG::Goodie;
use Text::FIGlet; use Text::FIGlet;
triggers startend => "figlet", "bigtext", "big text"; triggers startend => "figlet", "bigtext", "big text";
primary_example_queries 'figlet DuckDuckGo';
secondary_example_queries 'figlet computer DuckDuckGo';
name 'FIGlet';
description 'Uses FIGlet to make large letters out of ordinary text.';
category 'transformations';
topics 'words_and_games';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Figlet.pm';
attribution
web => ['http://engvik.nu', 'Lars Jansøn Engvik'],
github => [ 'larseng', 'Lars Jansøn Engvik'];
zci answer_type => 'figlet'; zci answer_type => 'figlet';
zci is_cached => 1; zci is_cached => 1;
@ -31,43 +20,53 @@ closedir DIR;
# Renders a figlet. # Renders a figlet.
sub render_figlet { sub render_figlet {
my ($font, $text) = @_; my ($font, $text) = @_;
return Text::FIGlet->new(-f=>$font, -d=>share())->figify(-w=>$width, -A=>$text); return Text::FIGlet->new(-f=>$font, -d=>share())->figify(-w=>$width, -A=>$text);
} }
handle query => sub { handle query => sub {
my $font; my $font;
my $text; my $text;
my $figlet; my $figlet;
my $html;
# Return if no input provided. # Return if no input provided.
return if ((lc $_ eq 'figlet') || (lc $_ eq 'bigtext') || (lc $_ eq 'big text')); return if ((lc $_ eq 'figlet') || (lc $_ eq 'bigtext') || (lc $_ eq 'big text'));
# Parse query. # Parse query.
$_ =~ m/^(?:figlet|bigtext|big text)(?:\-|\s+)(.*)|(.*)\s+(?:figlet|bigtext|big text)$/i; $_ =~ m/^(?:figlet|bigtext|big text)(?:\-|\s+)(.*)|(.*)\s+(?:figlet|bigtext|big text)$/i;
$text = $1 if $1; $text = $1 if $1;
$text = $2 if $2; $text = $2 if $2;
# Checks if the first word is a font. # Checks if the first word is a font.
$text =~ m/^\s*(\w+)/; $text =~ m/^\s*(\w+)/;
$font = lc $1 if grep /\b$1\b/i, @fonts; $font = lc $1 if grep /\b$1\b/i, @fonts;
# Strip the font from the text to render if we're using a font. # Strip the font from the text to render if we're using a font.
if ($font && $font ne $text) { if ($font && $font ne $text) {
$text = substr $text, length ($font)+1, length $text; $text = substr $text, length ($font)+1, length $text;
} else { } else {
$font = "standard"; $font = "standard";
} }
# Render the FIGlet # Render the FIGlet
$figlet = render_figlet($font, $text); $figlet = render_figlet($font, $text);
$figlet = html_enc($figlet) if grep /^$font$/i, qw(rot13 mnemonic term); #ensure we escape input for plaintext formatted results
$html = "<div id='figlet-wrapper'><span>Font: </span><span id='figlet-font'>$font</span><pre contenteditable='true'>$figlet</pre></div>"; return unless $figlet;
return $figlet, html => $html if $figlet; if ($font eq 'rot13' || $font eq 'mnemonic' || $font eq 'term') {
return; $figlet = html_enc($figlet);
}
return $figlet,
structured_answer => {
data => {
title => $figlet,
subtitle => "Font: $font",
},
templates => {
group => 'text',
}
};
}; };
1; 1;

View File

@ -1,44 +0,0 @@
package DDG::Goodie::Factorial;
# ABSTRACT: n factorial
use strict;
use DDG::Goodie;
use List::Util qw(reduce);
use bigint;
zci answer_type => "factorial";
zci is_cached => 1;
name "Factorial";
description "Returns Factorial of n";
primary_example_queries "factorial 7", "7 factorial";
secondary_example_queries "12 fact";
topics 'math';
category 'calculations';
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Factorial.pm";
attribution github => ["https://github.com/codenirvana", "Udit Vasu"],
twitter => "uditdistro";
triggers any => "factorial", "fact";
handle remainder => sub {
my $n = $_;
return unless $n =~ /^\d+$/;
$n = int($n);
my $fact = '1';
$fact = reduce { $a * $b } 1 .. $n if ($n > 0);
return "Factorial of $n is $fact",
structured_answer => {
input => [$n],
operation => 'Factorial',
result => $fact
};
};
1;

23
lib/DDG/Goodie/Factors.pm Normal file → Executable file
View File

@ -11,26 +11,21 @@ zci is_cached => 1;
triggers startend => 'factors', 'factors of'; triggers startend => 'factors', 'factors of';
primary_example_queries 'factors of 30';
secondary_example_queries '72 factors';
description 'Returns the factors of the entered number';
name 'Factors';
topics 'math';
category 'calculations';
attribution github => [ 'https://github.com/austinheimark', 'Austin Heimark' ];
handle remainder => sub { handle remainder => sub {
my $query = $_; my $query = $_;
return unless $query =~ /^\d+$/; return unless $query =~ /^\d+$/;
my $factors = join ', ', divisors($query); my $factors = join ', ', divisors($query);
return "Factors of $query: $factors", return "Factors of $query: $factors", structured_answer => {
structured_answer => { data => {
input => [$query], title => $factors,
operation => 'Factors', subtitle => "Factors of: $query"
result => $factors },
}; templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -7,18 +7,6 @@ use DDG::Goodie;
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "fedex"; zci answer_type => "fedex";
primary_example_queries "fedex 9241990100130206401644";
secondary_example_queries "federal express 9241990100130206401644";
description "Track a FedEx package";
name "FedEx";
icon_url "/i/fedex.com.ico";
source "FedEx";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FedEx.pm";
category 'ids';
topics 'special_interest';
attribution github => [ 'https://github.com/duckduckgo', 'duckduckgo'];
# Regex for fedex. # Regex for fedex.
my $fedex_qr = qr/fed(?:eral|)ex(?:press|)/io; my $fedex_qr = qr/fed(?:eral|)ex(?:press|)/io;
my $tracking_qr = qr/package|track(?:ing|)|num(?:ber|)|\#/i; my $tracking_qr = qr/package|track(?:ing|)|num(?:ber|)|\#/i;

View File

@ -12,16 +12,6 @@ with 'DDG::GoodieRole::Chess';
zci answer_type => "fen_viewer"; zci answer_type => "fen_viewer";
zci is_cached => 1; zci is_cached => 1;
name "FenViewer";
description "This instant answer parses a chess position in the Forsyth-Edwards notation, and draws a chessboard on screen representing that position.";
primary_example_queries "FEN rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "fen rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1";
category "entertainment";
topics "gaming";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FENViewer.pm";
attribution github => ["rouzbeh", "Ali Neishabouri"],
twitter => "Rou7_beh";
triggers start => "fen"; triggers start => "fen";
handle remainder => sub { handle remainder => sub {

27
lib/DDG/Goodie/Fibonacci.pm Normal file → Executable file
View File

@ -11,14 +11,6 @@ triggers any => 'fib', 'fibonacci';
zci answer_type => 'fibonacci'; zci answer_type => 'fibonacci';
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'fib 7';
secondary_example_queries 'fibonacci 33';
description 'Returns the n-th element of Fibonacci sequence';
attribution github => ['https://github.com/koosha--', 'koosha--'];
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Fibonacci.pm';
topics 'math';
category 'calculations';
my @fib = (0, 1); my @fib = (0, 1);
handle remainder => sub { handle remainder => sub {
@ -26,8 +18,7 @@ handle remainder => sub {
s/\s+$//; s/\s+$//;
return unless /^(?:what(?:'s| is) the )?(?<which>\d+)(?:th|rd|st)?(?: number)?(?: in the (?:series|sequence))?\??$/ && $1 <= 1470; return unless /^(?:what(?:'s| is) the )?(?<which>\d+)(?:th|rd|st)?(?: number)?(?: in the (?:series|sequence))?\??$/ && $1 <= 1470;
my $n = $+{'which'}; my $n = $+{'which'};
# Instead of calling a typical recursive function,
# use simple dynamic programming to improve performance
if ($#fib < $n) { if ($#fib < $n) {
for my $i ($#fib .. $n) { for my $i ($#fib .. $n) {
$fib[$i] = $fib[$i - 1] + $fib[$i - 2]; $fib[$i] = $fib[$i - 1] + $fib[$i - 2];
@ -35,12 +26,16 @@ handle remainder => sub {
} }
my $suf = ordsuf($n); my $suf = ordsuf($n);
return "The $n$suf fibonacci number is ${fib[$n]} (assuming f(0) = 0).", my $text_answer ="The $n$suf fibonacci number is ${fib[$n]} (assuming f(0) = 0).";
structured_answer => { return $text_answer, structured_answer => {
input => [$n . $suf], data => {
operation => 'Fibonacci number', title => $fib[$n],
result => $fib[$n], subtitle => "$n$suf Fibonacci number"
}; },
templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -6,17 +6,6 @@ use DDG::Goodie;
use YAML::XS 'LoadFile'; use YAML::XS 'LoadFile';
use Text::Trim; use Text::Trim;
primary_example_queries 'firefoxos alarm api';
secondary_example_queries 'fxos api contacts';
name 'Firefox OS device APIs';
description 'Get details of the Firefox OS device APIs.';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FirefoxOS.pm';
category 'programming';
topics 'programming';
attribution github => ['tagawa', 'Daniel Davis'],
twitter => ['ourmaninjapan', 'Daniel Davis'],
web => ['http://daniemon.com', 'Daniel Davis'];
triggers any => 'fxos', 'firefoxos', 'firefox os'; triggers any => 'fxos', 'firefoxos', 'firefox os';
zci answer_type => 'firefoxos'; zci answer_type => 'firefoxos';
@ -53,8 +42,6 @@ handle remainder => sub {
return $api->{permission}, return $api->{permission},
structured_answer => { structured_answer => {
id => "firefox_os",
name => "About",
data => { data => {
title => $api->{name}, title => $api->{name},
subtitle => "Manifest permission: " . $api->{permission}, subtitle => "Manifest permission: " . $api->{permission},

View File

@ -11,17 +11,6 @@ triggers startend => "flip text", "mirror text", "spin text", "rotate text";
zci answer_type => "flip_text"; zci answer_type => "flip_text";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'flip text sentence';
secondary_example_queries 'mirror text hello';
description 'flip and mirror text';
name 'FlipText';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FlipText.pm';
category 'transformations';
topics 'words_and_games';
attribution web => ['robert.io', 'Robert Picard'],
github => ['http://github.com/rpicard', 'rpicard'],
twitter => ['http://twitter.com/__rlp', '__rlp'];
handle remainder => sub { handle remainder => sub {
my $input = $_; my $input = $_;
@ -31,9 +20,13 @@ handle remainder => sub {
return $result, return $result,
structured_answer => { structured_answer => {
input => [html_enc($input)], data => {
operation => 'Flip text', title => $result,
result => html_enc($result), subtitle => "Flip text $input"
},
templates => {
group => 'text',
}
}; };
}; };

24
lib/DDG/Goodie/Fortune.pm Normal file → Executable file
View File

@ -7,15 +7,6 @@ use Fortune;
triggers startend => 'unix fortune', 'fortune cookie', 'random fortune'; triggers startend => 'unix fortune', 'fortune cookie', 'random fortune';
primary_example_queries 'fortune cookie', 'random fortune';
name 'Fortune';
description 'get a random phrase from the original fortunes file';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Fortune.pm';
category 'random';
topics 'words_and_games';
attribution github => ['https://github.com/frncscgmz', 'frncscgmz'],
github => ['https://github.com/dmschulman', 'dmschulman'];
zci answer_type => "fortune"; zci answer_type => "fortune";
zci is_cached => 0; zci is_cached => 0;
@ -27,12 +18,15 @@ handle remainder => sub {
my $output = $fortune_file->get_random_fortune(); my $output = $fortune_file->get_random_fortune();
$output =~ s/\n/ /g; $output =~ s/\n/ /g;
return $output, return $output, structured_answer => {
structured_answer => { data => {
input => [], title => $output,
operation => 'Random fortune', subtitle => "Random Fortune"
result => $output },
}; templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -6,15 +6,6 @@ use DDG::Goodie;
triggers start => 'frequency', 'freq'; triggers start => 'frequency', 'freq';
primary_example_queries 'frequency of all characters in testing';
secondary_example_queries 'frequency of B in battle', 'frequency of all in testing 1234 ABC!';
description 'calculate the frequency of characters in a string';
name 'Frequency';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Frequency.pm';
category 'calculations';
topics 'geek';
attribution github => [ 'http://github.com/unlisted', 'Morgan' ];
handle remainder => sub { handle remainder => sub {
if ($_ =~ /^of ([a-z]|(?:all ?|)(?:letters|characters|chars|)) in (.+)/i) if ($_ =~ /^of ([a-z]|(?:all ?|)(?:letters|characters|chars|)) in (.+)/i)
{ {

View File

@ -10,82 +10,8 @@ use Math::SigFigs qw(:all);
zci answer_type => "frequency_spectrum"; zci answer_type => "frequency_spectrum";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries '50 hz';
secondary_example_queries '400 thz';
description 'Returns information about light, radio, and sound frequencies';
name 'FrequencySpectrum';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/FrequencySpectrum.pm';
category 'physical_properties';
topics 'science';
attribution web => "https://machinepublishers.com", twitter => 'machinepub';
#Javascript to dynamically resize and/or hide elements #Javascript to dynamically resize and/or hide elements
my $dynamicwidths = <<EOF; my $dynamicwidths = "";
<script type="text/javascript">
// Get the marker label and tag
var markerlabel, markertag
markerlabel = document.getElementById("marker_label")
markertag = document.getElementById("marker_tag")
// Firefox (and possbily other browers) have a problem with the
// getBBox function. For now, I'll work around this by simply
// hiding the marker tag if getBBox() is not available.
try {
// Resize marker to fit text
bbox = markerlabel.getBBox()
markerlabel.setAttribute("x", bbox.x)
markertag.setAttribute("x", bbox.x - (bbox.width / 2))
markertag.setAttribute("y", bbox.y + 1)
markertag.setAttribute("width", bbox.width)
markertag.setAttribute("height", bbox.height)
// If the marker tag is wider than the window - 80 px, hide it
if (bbox.width > (wwidth - 80)) {
markerlabel.style.visibility = "hidden"
markertag.style.visibility = "hidden"
}
// If getBBox() not available, hide the tag and label
} catch(err) {
markerlabel.style.visibility = "hidden"
markertag.style.visibility = "hidden"
}
// When window is too small, remove marker label and tag
// and abbreviate major range (y-axis) labels
var wwidth, majrangelabels
wwidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
majrangelabels = document.getElementsByClassName("major_range_label")
if (wwidth < 500) {
// Marker tag and label
markerlabel.style.visibility = "hidden"
markertag.style.visibility = "hidden"
// Major range labels
for (var i = majrangelabels.length - 1; i >= 0; i--) {
var labeltext
labeltext = majrangelabels[i].childNodes[1].childNodes[0].textContent
if (labeltext === "Radio") {
majrangelabels[i].childNodes[1].childNodes[0].textContent = "Rad."
} else if (labeltext === "Infrared") {
majrangelabels[i].childNodes[1].childNodes[0].textContent = "Inf."
} else if (labeltext === "Visible light") {
majrangelabels[i].childNodes[1].childNodes[0].textContent = "Vis."
} else if (labeltext === "Ultraviolet") {
majrangelabels[i].childNodes[1].childNodes[0].textContent = "UV"
} else if (labeltext === "X-ray") {
majrangelabels[i].childNodes[1].childNodes[0].textContent = "X-ray"
} else if (labeltext === "Gamma") {
majrangelabels[i].childNodes[1].childNodes[0].textContent = "Gam."
}
}
}
</script>
EOF
#Regex to match a valid query #Regex to match a valid query
# Used for trigger and later for parsing # Used for trigger and later for parsing
@ -202,6 +128,7 @@ handle query => sub {
my $freq_formatted; my $freq_formatted;
my $answer; my $answer;
my $html; my $html;
my $plot;
#If wavelength provided, convert to frequency in hz #If wavelength provided, convert to frequency in hz
if ($wavelength) { if ($wavelength) {
@ -241,7 +168,7 @@ handle query => sub {
my $subspectrum = $emMatch->{subspectrum}; my $subspectrum = $emMatch->{subspectrum};
#Set up the plot panel #Set up the plot panel
my $plot = generate_plot($rangeMin, $rangeMax, scalar keys %emSpectrum); $plot = generate_plot($rangeMin, $rangeMax, scalar keys %emSpectrum);
#Add a major range for each subspectrum (e.g. radio or UV) #Add a major range for each subspectrum (e.g. radio or UV)
foreach (sort {$emSpectrum{$a}{'track'} <=> $emSpectrum{$b}{'track'} } keys %emSpectrum) { foreach (sort {$emSpectrum{$a}{'track'} <=> $emSpectrum{$b}{'track'} } keys %emSpectrum) {
@ -316,7 +243,24 @@ handle query => sub {
$html .= $plot->xmlify; $html .= $plot->xmlify;
} }
return $answer, html => wrap_html($html) if $answer; $plot->{svg} = "";
$plot->{transform} = "";
my @temp = $plot;
return "$answer", #html => wrap_html($html) if $answer;
structured_answer => {
data => {
title => $answer,
plot => $plot,
},
templates => {
group => 'text',
item => 0,
options => {
content => 'DDH.frequency_spectrum.content'
}
}
};
return; return;
}; };
@ -403,24 +347,25 @@ sub generate_plot {
} }
#Add plot background #Add plot background
$plot->{svg}->group( # $plot->{svg}->group(
class => 'plot_background', # class => 'plot_background',
)->rect( # )->rect(
width => '100%', # width => '100%',
height => $plot->{height}, # height => $plot->{height},
x => 0, # x => 0,
y => 0 # y => 0
); # );
#Add panel background #Add panel background
$plot->{svg}->group( # $plot->{svg}->group(
class => 'plot_panel', # class => 'plot_panel',
)->rect( # )->rect(
width => ($plot->{width} - $plot->{leftGutter} - $plot->{rightGutter}) . '%', # width => ($plot->{width} - $plot->{leftGutter} - $plot->{rightGutter}) . '%',
height => $plot->{panelHeight}, # height => $plot->{panelHeight},
x => $plot->{leftGutter} . '%', # x => $plot->{leftGutter} . '%',
y => $plot->{topGutter}, # y => $plot->{topGutter},
); # );
$plot->{plot_panel_width} = ($plot->{width} - $plot->{leftGutter} - $plot->{rightGutter});
#Calculate x-axis tick locations #Calculate x-axis tick locations
my @ticks; my @ticks;
@ -446,12 +391,12 @@ sub generate_plot {
} }
#Draw ticks #Draw ticks
my $xAxis = $plot->{svg}->group ( # my $xAxis = $plot->{svg}->group (
id => 'x_axis', # id => 'x_axis',
); # );
foreach (@ticks) { # foreach (@ticks) {
my $x = $plot->{transform}->($_); # my $x = $plot->{transform}->($_);
#Draw tick line #Draw tick line
# NOTE: Currently skipping this per wtrsld's redesign # NOTE: Currently skipping this per wtrsld's redesign
@ -465,27 +410,41 @@ sub generate_plot {
#); #);
#Annotate tick #Annotate tick
my $text = $xAxis->text( # my $text = $xAxis->text(
dy => '1em', # dy => '1em',
x => $x . '%', # x => $x . '%',
y => $plot->{panelHeight} + $plot->{topGutter} + 4, # y => $plot->{panelHeight} + $plot->{topGutter} + 4,
'text-anchor' => 'middle', # 'text-anchor' => 'middle',
class => 'x_axis_text' # class => 'x_axis_text'
); # );
# if ($log10 && $_ > 10) {
# $text->tag('tspan', -cdata => '10');
# $text->tag(
# 'tspan',
# 'baseline-shift' => 'super',
# dy => '-0.2em', #Superscripts need an extra nudge
# dx => '-0.5em', #Bring superscript close to parent
# -cdata => log10($_),
# style => { 'font-size' => '0.5em' },
# );
# } else {
# $text->tag('tspan', -cdata => $_);
# }
# }
my @x_axis;
foreach (@ticks) {
my $tspan_cdata;
if ($log10 && $_ > 10) { if ($log10 && $_ > 10) {
$text->tag('tspan', -cdata => '10'); $tspan_cdata = log10($_);
$text->tag(
'tspan',
'baseline-shift' => 'super',
dy => '-0.2em', #Superscripts need an extra nudge
dx => '-0.5em', #Bring superscript close to parent
-cdata => log10($_),
style => { 'font-size' => '0.5em' },
);
} else {
$text->tag('tspan', -cdata => $_);
} }
my $x_axis = {
x => $plot->{transform}->($_),
y => $plot->{panelHeight} + $plot->{topGutter} + 4,
tspan_cdata => $tspan_cdata,
};
push @x_axis, $x_axis;
} }
$plot->{x_axis} = \@x_axis;
#Add x-axis gridlines #Add x-axis gridlines
# NOTE: Currently skipping this per wtrsld's redesign # NOTE: Currently skipping this per wtrsld's redesign
@ -504,33 +463,46 @@ sub generate_plot {
#} #}
#Add a label to the x-axis #Add a label to the x-axis
my $xAxisLabel = $xAxis->text( # my $xAxisLabel = $xAxis->text(
dy => '1em', # dy => '1em',
x => '50%', # x => '50%',
y => $plot->{panelHeight} + $plot->{topGutter} + 25, # y => $plot->{panelHeight} + $plot->{topGutter} + 25,
'text-anchor' => 'middle', # 'text-anchor' => 'middle',
class => 'x_axis_label' # class => 'x_axis_label'
); # );
$xAxisLabel->tag('tspan', -cdata => 'Frequency (Hz)'); # $xAxisLabel->tag('tspan', -cdata => 'Frequency (Hz)');
$plot->{axis_label_y} = $plot->{panelHeight} + $plot->{topGutter} + 25;
#Add axis lines #Add axis lines
my $axislines = $plot->{svg}->group ( # my $axislines = $plot->{svg}->group (
class => 'axis_line', # class => 'axis_line',
); # );
my $xaxisline = $axislines->group(); # my $xaxisline = $axislines->group();
$xaxisline->line( # $xaxisline->line(
x1 => $plot->{transform}->(0) . '%', # x1 => $plot->{transform}->(0) . '%',
x2 => $plot->{transform}->($plot->{rangeMax}) . '%', # x2 => $plot->{transform}->($plot->{rangeMax}) . '%',
# y1 => $plot->{panelHeight} + $plot->{topGutter},
# y2 => $plot->{panelHeight} + $plot->{topGutter}
# );
# my $yaxisline = $axislines->group();
# $yaxisline->line(
# x1 => $plot->{transform}->(0) . '%',
# x2 => $plot->{transform}->(0) . '%',
# y1 => $plot->{topGutter},
# y2 => $plot->{topGutter} + $plot->{panelHeight}
# );
$plot->{x_axis_line} = {
x1 => $plot->{transform}->(0),
x2 => $plot->{transform}->($plot->{rangeMax}),
y1 => $plot->{panelHeight} + $plot->{topGutter}, y1 => $plot->{panelHeight} + $plot->{topGutter},
y2 => $plot->{panelHeight} + $plot->{topGutter} y2 => $plot->{panelHeight} + $plot->{topGutter}
); };
my $yaxisline = $axislines->group(); $plot->{y_axis_line} = {
$yaxisline->line( x1 => $plot->{transform}->(0),
x1 => $plot->{transform}->(0) . '%', x2 => $plot->{transform}->(0),
x2 => $plot->{transform}->(0) . '%',
y1 => $plot->{topGutter}, y1 => $plot->{topGutter},
y2 => $plot->{topGutter} + $plot->{panelHeight} y2 => $plot->{topGutter} + $plot->{panelHeight}
); };
return($plot); return($plot);
} }
@ -597,51 +569,58 @@ sub add_marker {
my $markerWidth = 1; #This is dynamically resized by $dynamicwidths my $markerWidth = 1; #This is dynamically resized by $dynamicwidths
my $markerHeight = 14; my $markerHeight = 14;
my $markerGutter = ($plot->{topGutter} - $markerHeight - 1) / 2; my $markerGutter = ($plot->{topGutter} - $markerHeight - 1) / 2;
$plot->{svg}->group( # $plot->{svg}->group(
class => 'marker_tag', # class => 'marker_tag',
)->rect( # )->rect(
id => 'marker_tag', # id => 'marker_tag',
width => $markerWidth, # width => $markerWidth,
height => $markerHeight, # height => $markerHeight,
x => $plot->{transform}->($markerValue) - ($markerWidth / 2) . '%', # x => $plot->{transform}->($markerValue) - ($markerWidth / 2) . '%',
y => $plot->{topGutter} - $markerGutter - $markerHeight + 1, #Extra pixel to account for plot border # y => $plot->{topGutter} - $markerGutter - $markerHeight + 1, #Extra pixel to account for plot border
style => { 'fill' => $RGB } # style => { 'fill' => $RGB }
); # );
#Add marker label #Add marker label
my $markerLabel = $plot->{svg}->group(); # my $markerLabel = $plot->{svg}->group();
my $markerLabelText = $markerLabel->text( # my $markerLabelText = $markerLabel->text(
x => $plot->{transform}->($markerValue) . '%', # x => $plot->{transform}->($markerValue) . '%',
y => $plot->{topGutter} - $markerGutter - ($markerHeight / 2) + 4, # y => $plot->{topGutter} - $markerGutter - ($markerHeight / 2) + 4,
'text-anchor' => 'middle', # 'text-anchor' => 'middle',
class => 'marker_label' # class => 'marker_label'
); # );
$markerLabelText->tag('tspan', id => 'marker_label', -cdata => ucfirst($freq_formatted)); # $markerLabelText->tag('tspan', id => 'marker_label', -cdata => ucfirst($freq_formatted));
#Add marker line #Add marker line
$plot->{svg}->group( # $plot->{svg}->group(
class => 'marker' # class => 'marker'
)->line( # )->line(
id => 'marker', # id => 'marker',
x1 => $plot->{transform}->($markerValue) . '%', # x1 => $plot->{transform}->($markerValue) . '%',
x2 => $plot->{transform}->($markerValue) . '%', # x2 => $plot->{transform}->($markerValue) . '%',
# y1 => $plot->{topGutter} - $markerGutter,
# y2 => $plot->{topGutter} + $plot->{panelHeight},
# style => { 'stroke' => $RGB },
# );
$plot->{marker_line} = {
x1 => $plot->{transform}->($markerValue),
x2 => $plot->{transform}->($markerValue),
y1 => $plot->{topGutter} - $markerGutter, y1 => $plot->{topGutter} - $markerGutter,
y2 => $plot->{topGutter} + $plot->{panelHeight}, y2 => $plot->{topGutter} + $plot->{panelHeight},
style => { 'stroke' => $RGB }, color => $RGB,
); };
return $plot; return $plot;
} }
#Wrap html #Wrap html
sub wrap_html { #sub wrap_html {
return <<EOF; # return <<EOF;
<!--[if gte IE 9]><!--> #<!--[if gte IE 9]><!-->
<div class='zci--conversions text--primary'>$_[0]</div> #<div class='zci--conversions text--primary'>$_[0]</div>
<![endif]--> #<![endif]-->
$dynamicwidths #$dynamicwidths
EOF #EOF
} #}
#Get log10 of a number #Get log10 of a number
sub log10 { sub log10 {

View File

@ -22,18 +22,6 @@ triggers any => keys %guid;
zci answer_type => "guid"; zci answer_type => "guid";
zci is_cached => 0; zci is_cached => 0;
primary_example_queries 'guid';
secondary_example_queries 'uuid';
description 'generate a unique indentifier';
name 'GUID';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GUID.pm';
category 'computing_tools';
topics 'programming';
attribution twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'],
github => 'loganom';
handle remainder => sub { handle remainder => sub {
s/$allowedTriggers//g; # strip allowed triggers s/$allowedTriggers//g; # strip allowed triggers

View File

@ -6,17 +6,6 @@ use DDG::Goodie;
zci answer_type => "2048"; zci answer_type => "2048";
zci is_cached => 1; zci is_cached => 1;
name "2048";
description "Javascript IA for online 2048";
primary_example_queries "play 512";
secondary_example_queries "play 4096";
category "entertainment";
topics "gaming";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Game2048.pm";
attribution
github => ["Roysten", "Roy van der Vegt"],
github => ["https://github.com/puskin94", "puskin"];
my @game_modes = qw (128 256 512 1024 2048 4096 8192); my @game_modes = qw (128 256 512 1024 2048 4096 8192);
my @triggers = map { "play $_" } @game_modes; my @triggers = map { "play $_" } @game_modes;
triggers start => @triggers; triggers start => @triggers;
@ -35,8 +24,6 @@ handle query_lc => sub {
return $text, return $text,
structured_answer => { structured_answer => {
id => 'game2048',
name => '2048',
data => { data => {
title => $text, title => $text,
subtitle => $dimension . 'x' . $dimension, subtitle => $dimension . 'x' . $dimension,

22
lib/DDG/Goodie/GenerateMAC.pm Normal file → Executable file
View File

@ -14,25 +14,21 @@ triggers startend => "generate mac addr",
zci answer_type => "MAC Address"; zci answer_type => "MAC Address";
zci is_cached => 0; zci is_cached => 0;
primary_example_queries 'please generate mac address';
description 'generates a MAC address';
name "GenerateMAC";
attribution github => ['https://github.com/UnGround', 'Charlie Belmer'],
web => ['http://www.charliebelmer.com', 'Charlie Belmer'];
handle remainder => sub { handle remainder => sub {
# Ensure rand is seeded for each process # Ensure rand is seeded for each process
srand(); srand();
my $address = join(':', map { sprintf '%0.2X', rand(255) } (1 .. 6)); my $address = join(':', map { sprintf '%0.2X', rand(255) } (1 .. 6));
return "Here's a random MAC address: $address", return "Here's a random MAC address: $address", structured_answer => {
structured_answer => { data => {
input => [], title => $address,
operation => 'Random MAC address', subtitle => 'Random MAC Address'
result => $address },
}; templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -0,0 +1,101 @@
package DDG::Goodie::GibberishGenerator;
# ABSTRACT: generates random gibberish
use strict;
use DDG::Goodie;
use utf8;
use Silly::Werder qw(sentence get_werd);
use Lingua::EN::Numericalize;
triggers any => qw(nonsense gibberish);
zci is_cached => 0;
zci answer_type => "gibberish_generator";
my %languages = (
'english' => ['English', 'small'],
'german' => ['German', ''],
'french' => ['French', ''],
'shakespearean' => ['Shakespeare', ''],
'swedish' => ['Swedish', ''],
);
sub pluralise {
my ($amount, $to_pluralise) = @_;
return $amount == 1 ? $to_pluralise : ($to_pluralise . 's');
}
my $types = qr/(?<type>word|sentence|line)/i;
my $modifier_re = qr/(?<modifier>English|German|French|Swedish|Shakespearean)/i;
my $amountre = qr/(?<amount>\d+)/;
my $nonsense_re = qr/(?:nonsense|gibberish)/i;
my $forms = qr/^(?:
(((?<amount>\d+)\s)?($types)s?\sof((?!$modifier_re).)*(?<modifier>$modifier_re)?+.*($nonsense_re))
|((?<amount>\d+)\s(?<modifier>$modifier_re)?\s*($nonsense_re)\s($types)s?)
)$/xi;
# Generates a string containing all the nonsense words/sentences.
sub generate_werds {
my ($amount, $modifier, $type) = @_;
my @modifier_args = @{$languages{$modifier}};
my $werd = Silly::Werder->new();
$werd->set_language(@modifier_args);
my $werds;
if ($type eq 'sentence') {
$werds = join ' ', map { $werd->sentence() } (1..$amount);
} elsif ($type eq 'word') {
$werd->set_werds_num($amount, $amount);
$werds = $werd->sentence();
} elsif ($type eq 'line') {
$werds = join "\n", map { $werd->line() } (1..$amount);
};
return $werds;
}
sub get_approximate_amount_werds {
my ($num, $type) = @_;
return $num if $type eq 'word';
return $num * 8 if $type eq 'sentence';
return $num * 8 if $type eq 'line';
}
handle query_lc => sub {
my $query = $_;
# Treat 'a' as 'one' for the purposes of amounts.
$query =~ s/^a /one /i;
# Convert initial words into numbers if possible.
my $formatted = $query =~ s/^\w+[a-rt-z](?=\b)/str2nbr($&)/ier;
return unless $formatted =~ $forms;
my $amount = $+{'amount'} // 1;
my $modifier = $+{'modifier'} // 'english';
my $type = $+{'type'};
return if get_approximate_amount_werds($amount, $type) > 200;
my $result = generate_werds($amount, $modifier, $type);
# Proper-case modifier (english -> English)
my $fmodifier = $modifier =~ s/^\w/\u$&/r;
# E.g, "3 words of Swedish gibberish"
my $formatted_input = "$amount @{[pluralise $amount, $type]} of $fmodifier gibberish";
my @paragraphs = split "\n", $result;
return $result, structured_answer => {
data => {
title => "$formatted_input",
gibberish_paragraphs => \@paragraphs,
use_paragraphs => $#paragraphs ? 1 : 0,
},
templates => {
group => 'info',
options => {
moreAt => 0,
content => 'DDH.gibberish_generator.content',
chompContent => 1,
}
}
};
};
1;

View File

@ -10,16 +10,6 @@ zci is_cached => 1;
triggers start => "golden ratio"; triggers start => "golden ratio";
primary_example_queries 'golden ratio 900:?';
secondary_example_queries 'golden ratio ?:123.345';
description 'find the number in the golden ratio with a number';
name 'GoldenRatio';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/GoldenRatio.pm';
category 'calculations';
topics 'math';
attribution twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'];
handle remainder => sub { handle remainder => sub {
my $input = $_; my $input = $_;
my $golden_ratio = (1 + sqrt(5)) / 2; my $golden_ratio = (1 + sqrt(5)) / 2;

View File

@ -3,20 +3,13 @@ package DDG::Goodie::GreatestCommonFactor;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
use Math::BigInt try => 'GMP';
zci answer_type => "greatest_common_factor"; zci answer_type => "greatest_common_factor";
zci is_cached => 1; zci is_cached => 1;
triggers startend => 'greatest common factor', 'gcf', 'greatest common divisor', 'gcd'; triggers startend => 'greatest common factor', 'gcf', 'greatest common divisor', 'gcd';
primary_example_queries 'GCF 121 11';
secondary_example_queries '99 9 greatest common factor';
description 'returns the greatest common factor of the two entered numbers';
name 'GreatestCommonFactor';
topics 'math';
category 'calculations';
attribution github => [ 'https://github.com/austinheimark', 'Austin Heimark' ];
handle remainder => sub { handle remainder => sub {
return unless /^\s*\d+(?:(?:\s|,)+\d+)*\s*$/; return unless /^\s*\d+(?:(?:\s|,)+\d+)*\s*$/;
@ -30,23 +23,18 @@ handle remainder => sub {
my $formatted_numbers = join(', ', @numbers); my $formatted_numbers = join(', ', @numbers);
$formatted_numbers =~ s/, ([^,]*)$/ and $1/; $formatted_numbers =~ s/, ([^,]*)$/ and $1/;
my $result = shift @numbers; my $result = Math::BigInt::bgcd(@numbers);
foreach (@numbers) {
$result = gcf($result, $_)
}
return "Greatest common factor of $formatted_numbers is $result.", return "Greatest common factor of $formatted_numbers is $result.",
structured_answer => { structured_answer => {
input => [$formatted_numbers], data => {
operation => 'Greatest common factor', title => "$result",
result => $result subtitle => "Greatest common factor: $formatted_numbers"
},
templates => {
group => "text",
}
}; };
}; };
sub gcf {
my ($x, $y) = @_;
($x, $y) = ($y, $x % $y) while $y;
return $x;
}
1; 1;

View File

@ -14,17 +14,6 @@ our %chord_lists; #from chords.pm
zci answer_type => 'guitarchord'; zci answer_type => 'guitarchord';
zci is_cached => 1; zci is_cached => 1;
name "GuitarChords";
description "Returns diagrams of guitar chords";
category "reference";
topics "music";
primary_example_queries 'G#m6 guitar chord', 'G guitar chord', 'Dbdim guitar chord';
attribution github => ["charles-l", "Charles Saternos"],
twitter => ["theninjacharlie", "Charles Saternos"],
web => ["http://charels-l.github.io", "Charles Saternos"];
triggers startend => ('guitar chord'); triggers startend => ('guitar chord');
handle remainder => sub handle remainder => sub

View File

@ -7,15 +7,6 @@ use DDG::Goodie;
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "hkdk"; zci answer_type => "hkdk";
primary_example_queries 'CU123456789DK';
secondary_example_queries 'EE123456789HK';
description 'Track a package from Hongkong Post or Post Danmark';
name 'HK/DK';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/HKDK.pm';
category 'ids';
topics 'special_interest';
attribution github => [ 'https://github.com/duckduckgo', 'duckduckgo'];
triggers query_nowhitespace_nodash => qr/([a-z]{2}\d{9}(?:hk|dk))/i; triggers query_nowhitespace_nodash => qr/([a-z]{2}\d{9}(?:hk|dk))/i;
handle query_nowhitespace_nodash => sub { handle query_nowhitespace_nodash => sub {

View File

@ -13,18 +13,6 @@ use strict;
zci answer_type => 'html_entity'; zci answer_type => 'html_entity';
zci is_cached => 1; zci is_cached => 1;
triggers any => 'html', 'entity', 'htmldecode', 'decodehtml', 'htmlentity'; triggers any => 'html', 'entity', 'htmldecode', 'decodehtml', 'htmlentity';
primary_example_queries 'html decode &#33;', 'html decode &amp';
secondary_example_queries 'html entity &#x21' , '#36 decode html', 'what is the decoded html entity of &#36;';
description 'Decode HTML entities';
name 'HTMLEntitiesDecode';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/HTMLEntitiesDecode.pm';
category 'computing_tools';
topics 'programming';
attribution twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'],
twitter => ['https://twitter.com/nshanmugham', 'Nishanth Shanmugham'],
web => ['http://nishanths.github.io', 'Nishanth Shanmugham'],
github => ['https://github.com/nishanths', 'Nishanth Shanmugham'];
handle remainder => sub { handle remainder => sub {
$_ = trim $_; # remove front and back whitespace $_ = trim $_; # remove front and back whitespace

View File

@ -10,26 +10,9 @@ use utf8;
triggers any => 'html', 'entity', 'htmlencode','encodehtml','htmlescape','escapehtml', 'htmlentity'; triggers any => 'html', 'entity', 'htmlencode','encodehtml','htmlescape','escapehtml', 'htmlentity';
primary_example_queries 'html em dash', 'html entity A-acute', 'html escape &';
secondary_example_queries 'html code em-dash', 'html entity for E grave', '$ sign htmlentity', 'pound sign html encode', 'html character code for trademark symbol',
'what is the html entity for greater than sign', 'how to encode an apostrophe in html';
name 'HTMLEntitiesEncode';
description 'Displays the HTML entity code for the query name';
category 'cheat_sheets';
topics 'programming', 'web_design';
code_url 'https://github.com/duckduckgo/zeroclickinfo-spice/blob/master/lib/DDG/Goodie/HTMLEntitiesEncode.pm';
zci answer_type => 'html_entity'; zci answer_type => 'html_entity';
zci is_cached => 1; zci is_cached => 1;
attribution web => ["http://nishanths.github.io", "Nishanth Shanmugham"],
github => ["https://github.com/nishanths", "Nishanth Shanmugham"],
twitter => ["https://twitter.com/nshanmugham", "Nishanth Shanmugham"],
twitter => ['crazedpsyc','crazedpsyc'],
cpan => ['CRZEDPSYC','crazedpsyc'],
github => ['https://github.com/mintsoft', "Rob Emery"];
# '&' and ';' not included in the hash value -- they are added in make_text() and make_html() # '&' and ';' not included in the hash value -- they are added in make_text() and make_html()
my %codes = ( my %codes = (
# Punctuation # Punctuation
@ -257,8 +240,6 @@ sub make_structured_answer {
} }
return { return {
id => "htmlentitiesencode",
name => "HTML Entities",
templates => { templates => {
group => 'list', group => 'list',
options => { options => {

23
lib/DDG/Goodie/HelpLine.pm Normal file → Executable file
View File

@ -13,14 +13,6 @@ triggers any => @$triggers;
zci answer_type => 'helpline'; zci answer_type => 'helpline';
zci is_cached => 0; zci is_cached => 0;
primary_example_queries 'suicide hotline';
description 'Checks if a query with the word "suicide" was made and returns a 24 hr suicide hotline.';
attribution github => ['https://github.com/conorfl', 'conorfl'];
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Helpline.pm';
topics 'everyday';
category 'special';
source 'https://en.wikipedia.org/wiki/List_of_suicide_crisis_lines';
my $helplines = LoadFile(share('helplines.yml')); my $helplines = LoadFile(share('helplines.yml'));
my %suicide_phrases = map { $_ => 1 } @$triggers; my %suicide_phrases = map { $_ => 1 } @$triggers;
@ -39,12 +31,15 @@ handle query_lc => sub {
$operation .= 's' if (scalar @contacts > 1); $operation .= 's' if (scalar @contacts > 1);
$operation .= ' in ' . $helpline->{display_country}; $operation .= ' in ' . $helpline->{display_country};
return $operation . ": " . $numbers_string, return $operation . ": " . $numbers_string, structured_answer => {
structured_answer => { data => {
input => [], title => $numbers_string,
operation => $operation, subtitle => $operation
result => $numbers_string, },
}; templates => {
group => 'text'
}
};
}; };
1; 1;

View File

@ -7,24 +7,16 @@ use DDG::Goodie;
# Used to restrict long generated outputs # Used to restrict long generated outputs
use constant MAX_INPUT_CHARS => 128; use constant MAX_INPUT_CHARS => 128;
triggers start => 'ascii'; triggers startend => 'ascii', 'hex to ascii';
zci is_cached => 1; zci is_cached => 1;
zci answer_type => 'ascii'; zci answer_type => 'ascii';
primary_example_queries 'ascii 0x74657374';
secondary_example_queries 'ascii 0x5468697320697320612074657374';
description 'Return the ASCII representation of a given printable HEX number.';
name 'HexToASCII';
code_url 'http://github.com';
category 'computing_tools';
topics 'programming';
attribution github => ['https://github.com/koosha--', 'koosha--'];
handle remainder => sub { handle remainder => sub {
my $value = $_; my $value = $_;
$value =~ s/^\s+//; $value =~ s/^\s+//;
$value =~ s/\s+$//; $value =~ s/\s+$//;
$value =~ s/ (?:to|as|in)$//i;
if ($value =~ /^(?:[0\\]x)?([0-9a-f]+)$/i or $value =~ /^([0-9a-f]+)h?$/i) { if ($value =~ /^(?:[0\\]x)?([0-9a-f]+)$/i or $value =~ /^([0-9a-f]+)h?$/i) {
my @digits = $1 =~ /(..)/g; my @digits = $1 =~ /(..)/g;
my ($pure, $html, $count); my ($pure, $html, $count);
@ -43,12 +35,16 @@ handle remainder => sub {
$html .= '&hellip;'; $html .= '&hellip;';
} }
return $pure . ' (ASCII)', return $html,
structured_answer => { structured_answer => {
input => [$value], data => {
operation => 'Hex to ASCII', title => $html,
result => $html, subtitle => "Hex to ASCII: $value",
}; },
templates => {
group => 'text',
},
};
} }
return; return;
}; };

22
lib/DDG/Goodie/HexToDec.pm Normal file → Executable file
View File

@ -10,16 +10,6 @@ triggers query_raw => qr/^\s*0x[0-9a-fA-F]+(?:(?:\s+hex)?\s+(?:in|as|to)\s+(?:de
zci answer_type => 'hex_to_dec'; zci answer_type => 'hex_to_dec';
zci is_cached => 1; zci is_cached => 1;
primary_example_queries '0x44696f21';
description 'convert hexidecimal to decimal';
name 'HexToDec';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/HexToDec.pm';
category 'conversions';
topics 'math', 'programming';
attribution cpan => 'majuscule',
github => ['nospampleasemam', 'Dylan Lloyd'],
web => ['https://dylansserver.com', 'Dylan Lloyd'] ;
handle query_raw => sub { handle query_raw => sub {
return unless (m/0x([0-9a-fA-F]+)/); return unless (m/0x([0-9a-fA-F]+)/);
@ -27,10 +17,14 @@ handle query_raw => sub {
my $decimal = Math::BigInt->from_hex($hex); my $decimal = Math::BigInt->from_hex($hex);
return "$hex base 16 = $decimal base 10", structured_answer => { return "$hex base 16 = $decimal base 10", structured_answer => {
input => ['0x' . $hex], data => {
operation => 'Hex to decimal', title => "$decimal", # Quoted for display precision as string.
result => "$decimal", # Quoted for display precision as string. subtitle => "Hex to decimal: 0x" . $hex
},
templates => {
group => 'text',
}
}; };
}; };
0x41414141; 0x01;

View File

@ -6,18 +6,6 @@ use DDG::Goodie;
use Net::IDN::Encode ':all'; use Net::IDN::Encode ':all';
use utf8; use utf8;
primary_example_queries 'idn exämple.com';
secondary_example_queries 'idn xn--exmple-cua.com';
description 'encode and decode internationalized domain names';
name 'IDN';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IDN.pm';
topics 'travel';
category 'computing_tools';
attribution web => ['http://7bits.nl/', 'Peter van Dijk'],
email => ['peter@7bits.nl', 'Peter van Dijk'],
github => ['https://github.com/habbie', 'habbie'],
twitter => ['https://twitter.com/habbie', 'habbie'];
triggers startend => ( triggers startend => (
'idn', 'idn',
'international domain', 'international domain',

View File

@ -7,14 +7,6 @@ use DDG::Goodie;
zci is_cached => 1; zci is_cached => 1;
zci answer_type => "ips"; zci answer_type => "ips";
primary_example_queries 'EM999999999IN';
description 'Track a package from IPS';
name 'IPS';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IPS.pm';
category 'ids';
topics 'special_interest';
attribution github => [ 'https://github.com/duckduckgo', 'duckduckgo'];
triggers query_nowhitespace_nodash => qr/(E[MA]\d{9})(IN|HR|)/xi; triggers query_nowhitespace_nodash => qr/(E[MA]\d{9})(IN|HR|)/xi;
handle query_nowhitespace_nodash => sub { handle query_nowhitespace_nodash => sub {

25
lib/DDG/Goodie/ISO639.pm Normal file → Executable file
View File

@ -10,24 +10,17 @@ triggers start => "iso 639", "iso639", "language code";
zci answer_type => "iso639"; zci answer_type => "iso639";
zci is_cached => 1; zci is_cached => 1;
primary_example_queries 'iso639 english';
secondary_example_queries 'iso639 ab';
description 'lookup ISO639 language names and codes';
name 'ISO639';
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/ISO639.pm';
category 'reference';
topics 'programming';
attribution github => [ 'http://github.com/tantalor', 'John Tantalo' ],
web => [ 'http://johntantalo.com/blog', 'John Tantalo' ];
handle remainder => sub { handle remainder => sub {
my ($lang, $code) = langpair(shift) or return; my ($lang, $code) = langpair(shift) or return;
return 'ISO 639: '. $lang .' - '. $code,
structured_answer => { return "ISO 639: $lang - $code", structured_answer => {
input => [$lang], data => {
operation => 'ISO 639 Language code', title => $code,
result => ($code), subtitle => "ISO 639 Language code: $lang"
},
templates => {
group => 'text'
}
}; };
}; };

View File

@ -3,25 +3,13 @@ package DDG::Goodie::IndependenceDay;
use strict; use strict;
use DDG::Goodie; use DDG::Goodie;
use JSON; use JSON::MaybeXS;
use utf8; use utf8;
use Locale::Country; use Locale::Country;
zci answer_type => "independence_day"; zci answer_type => "independence_day";
zci is_cached => 1; zci is_cached => 1;
name "independence day";
description "Gives the date of when a nation assumed independence";
primary_example_queries "what is the independence day of norway", "independence day, papua new guinea";
category "dates";
topics "trivia";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IndependenceDay.pm";
attribution github => ["jarmokivekas", "Jarmo Kivekäs"],
web => ["http://guttula.com", "Jarmo Kivekäs"],
github => ["YouriAckx", "Youri Ackx"],
twitter => ["YouriAckx", "Youri Ackx"];
# Triggers # Triggers
triggers any => "independence day", "day of independence"; triggers any => "independence day", "day of independence";
@ -79,7 +67,6 @@ handle query_clean => sub {
return $text, return $text,
structured_answer => { structured_answer => {
id => 'independence_day',
templates => { templates => {
group => "icon", group => "icon",
item => 0, item => 0,

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::19kestier;
# ABSTRACT: 19kestier's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_19kestier";
zci is_cached => 1;
name "IsAwesome 19kestier";
description "My first Goodie, it let's the world know that 19kestier is awesome";
primary_example_queries "duckduckhack 19kestier";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/19kestier.pm";
attribution github => ["https://github.com/19kestier", "19kestier"],
twitter => "kestier19";
triggers start => "duckduckhack 19kestier";
handle remainder => sub {
return if $_;
return "19kestier is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,29 +0,0 @@
package DDG::Goodie::IsAwesome::5ika;
# ABSTRACT: 5ika' first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_5ika";
zci is_cached => 1;
name "IsAwesome 5ika";
description "Hey world, i'm awesome";
primary_example_queries "duckduckhack 5ika";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/5ika.pm";
attribution github => ["https://github.com/5ika/", "5ika"],
twitter => "05Sika";
# Triggers
triggers start => "duckduckhack 5ika";
# Handle statement
handle remainder => sub {
return if $_;
return "Sika is awesome !";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::AJDev77;
# ABSTRACT: AJDev77's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_ajdev77";
zci is_cached => 1;
name "IsAwesome AJDev77";
description "My first Goodie, it let's the world know that AJDev77 is awesome";
primary_example_queries "duckduckhack AJDev77";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/AJDev77.pm";
attribution github => ["https://github.com/AJDev77", "AJ"],
twitter => "emposoft";
triggers start => "duckduckhack ajdev77";
handle remainder => sub {
return if $_;
return "AJDev77 is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::BigxMac;
# ABSTRACT: BigxMac's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_bigx_mac";
zci is_cached => 1;
name "IsAwesome BigxMac";
description "My first Goodie, it let's the world know that BigxMac is awesome";
primary_example_queries "duckduckhack BigxMac";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/BigxMac.pm";
attribution github => ["BigxMac", "Jared S"],
twitter => "twitterhandle";
triggers start => "duckduckhack bigxmac";
handle remainder => sub {
return if $_;
return "BigxMac is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,28 +0,0 @@
package DDG::Goodie::IsAwesome::Bitflipped;
# ABSTRACT: Bitflipped's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_bitflipped";
zci is_cached => 1;
name "IsAwesome Bitflipped";
description "Prints nice message";
primary_example_queries "duckduckhack bitflipped";
secondary_example_queries "optional -- demonstrate any additional triggers";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/Bitflipped.pm";
attribution github => ["bitflipped", "Jon A"];
triggers start => "duckduckhack bitflipped";
handle remainder => sub {
return if $_; # Guard against "no answer"
return "bitflipped is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::Black616Angel;
# ABSTRACT: Black616Angels first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_black616angel";
zci is_cached => 1;
name "IsAwesome Black616Angel";
description "My first Goodie, it lets the world know that Black616Angel awesome";
primary_example_queries "duckduckhack Black616Angel";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/Black616Angel.pm";
attribution github => ["https://github.com/Black616Angel", "Black616Angel"],
;
triggers start => "duckduckhack black616angel";
handle remainder => sub {
return if $_;
return "Black616Angel is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::Blito;
# ABSTRACT: Blito's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_blito";
zci is_cached => 1;
name "IsAwesome Blito";
description "My first Goodie, it let's the world know that Blito is awesome";
primary_example_queries "duckduckhack Blito";
secondary_example_queries "optional -- demonstrate any additional triggers";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/Blito.pm";
attribution github => ["https://github.com/Blito", "Blito"];
triggers start => "duckduckhack blito";
handle remainder => sub {
return if $_;
return "Blito is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,27 +0,0 @@
package DDG::Goodie::IsAwesome::Blueprinter;
# ABSTRACT: Blueprinter's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_blueprinter";
zci is_cached => 1;
name "IsAwesome Blueprinter";
description "My first Goodie, it let's the world know that GitHubUsername is awesome";
primary_example_queries "duckduckhack Blueprinter";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/Blueprinter.pm";
attribution github => ["https://github.com/Blueprinter", "Blueprinter"];
triggers start => "duckduckhack blueprinter";
handle remainder => sub {
return if $_;
return "Blueprinter is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,25 +0,0 @@
package DDG::Goodie::IsAwesome::CalaveraTheNine;
# ABSTRACT: CalaveraTheNine's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_calavera_the_nine";
zci is_cached => 1;
name "IsAwesome CalaveraTheNine";
description "My first Goodie, it let's the world know that CalaveraTheNine is awesome";
primary_example_queries "duckduckhack CalaveraTheNine";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/CalaveraTheNine.pm";
attribution github => ["https://github.com/CalaveraTheNine", "CalaveraTheNine"];
triggers start => "duckduckhack calaverathenine";
handle remainder => sub {
return if $_;
return "CalaveraTheNine is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::DeanT765;
# ABSTRACT: DeanT765's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_dean_t765";
zci is_cached => 1;
name "IsAwesome DeanT765";
description "My first Goodie, it let's the world know that DeanT765 is awesome";
primary_example_queries "duckduckhack DeanT765";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/DeanT765.pm";
attribution github => ["https://github.com/DeanT765", "DeanT765"],
twitter => "deanthomasson";
triggers start => "duckduckhack deant765";
handle remainder => sub {
return if $_;
return "DeanT765 is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::Dronov;
# ABSTRACT: Dronov's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_dronov";
zci is_cached => 1;
name "Mikhail Dronov";
description "http://dronov.net";
primary_example_queries "duckduckhack dronov";
category "programming";
topics "geek", "programming";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/Dronov.pm";
attribution github => ["https://github.com/dronov", "dronov"],
twitter => "dronovmm";
triggers start => "duckduckhack dronov";
handle remainder => sub {
return if $_;
return "Mikhail Dronov is awesome and has successfully completed the DuckDuckHack Goodie tutorial! He's saying hello from far cold Russia. Find him at dronov.net";
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::Echosa;
# ABSTRACT: Echosa's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_echosa";
zci is_cached => 1;
name "IsAwesome Echosa";
description "My first Goodie. It lets the world know that Echosa is awesome.";
primary_example_queries "duckduckhack echosa";
category "special";
topics "special_interest", "geek";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/Echosa.pm";
attribution github => ["https://github.com/echosa", "Echosa"],
twitter => "echosa";
triggers start => "duckduckhack echosa";
handle remainder => sub {
return if $_;
return "Echosa is awesome and has successfully completed the DuckDuckHack Goodie tutorial!"
};
1;

View File

@ -1,26 +0,0 @@
package DDG::Goodie::IsAwesome::JohnCarlosReed;
# ABSTRACT: JohnCarlosReed's first Goodie
use DDG::Goodie;
use strict;
zci answer_type => "is_awesome_john_carlos_reed";
zci is_cached => 1;
name "IsAwesome JohnCarlosReed";
description "My first Goodie, it let's the world know that JohnCarlosReed is awesome";
primary_example_queries "duckduckhack JohnCarlosReed";
category "entertainment";
topics "entertainment", "special_interest";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/IsAwesome/JohnCarlosReed.pm";
attribution github => ["https://github.com/JohnCarlosReed", "John Reed"],
twitter => "johnnycarlos";
triggers start => "duckduckhack johncarlosreed";
handle remainder => sub {
return if $_;
return "JohnCarlosReed is awesome and has successfully completed the DuckDuckHack Goodie tutorial!";
};
1;

Some files were not shown because too many files have changed in this diff Show More