编写DuckDuckGo即时答案非常简单

编辑注:本文中的一些信息已经过时,请参阅我们的新文章《编写DuckDuckGo插件变得更简单》以获取详细信息。
几周前,我参加了纽约市的Quack & Hack,并学习了如何编写DuckDuckGo即时答案。即时答案真的很酷:它们是在用户搜索特定术语时触发的微型应用程序。例如,如果您搜索help tmux,您将看到tmux速查表显示。这太棒了——您可以将代码提交到DuckDuckGo.com,而且好消息是您不必等到下一次Quack & Hack,就可以自己学习如何编写;DuckDuckGo提供了使编写变得简单的优秀工具。
设置开发环境
DuckDuckGo支持多种不同类型的即时答案,但今天我将重点介绍创建速查表,当用户搜索匹配的一组关键字时,搜索引擎会显示速查表。
要开始,您需要Perl 5.18或更高版本,并已安装App::DuckPAN,您可以使用cpan
或cpanminus
来完成。
$ cpan App::DuckPAN
# or
$ cpanm App::DuckPAN
您还需要DuckDuckGo的即时答案repo repo的本地副本,您可以使用Git进行克隆
$ git clone https://github.com/duckduckgo/zeroclickinfo-goodies.git
在安装了App::DuckPAN和goodies repo之后,进入zeroclickinfo-goodies repo,并启动duckpan服务器
$ cd zeroclickinfo-goodies
$ duckpan server
当您运行duckpan server
时,可能会看到很多输出,但您应该看到这个
Checking asset cache...
Starting up webserver...
You can stop the webserver with Ctrl-C
HTTP::Server::PSGI: Accepting connections at http://0:5000/
如果您打开浏览器并导航到http://localhost:5000
,您将看到DuckDuckGo搜索页面(如果localhost不起作用,请尝试http://0:5000
)。搜索“help tmux”,您应该看到与实时网站上相同的即时答案速查表。
创建即时答案
现在,您已经设置了开发环境,您准备好创建即时答案了。我将为perldoc
创建一个即时答案(取自我的perldoc 文章)。我可以通过使用duckpan new
创建即时答案的骨架代码来提前开始。
$ duckpan new PerldocCheatSheet
这创建了即时答案所需的基本文件。
Created file: lib/DDG/Goodie/PerldocCheatSheet.pm
Created file: t/PerldocCheatSheet.t
Successfully created Goodie: PerldocCheatSheet
即时答案的所有逻辑都在PerldocCheatSheet.pm
中,并且duckpan已经创建了一个很好的骨架。
package DDG::Goodie::PerldocCheatSheet;
# ABSTRACT: Write an abstract here
# Start at https://duck.co/duckduckhack/goodie_overview if you are new
# to instant answer development
use DDG::Goodie;
zci answer_type => "perldoc_cheat_sheeet";
zci is_cached => 1;
# Metadata. See https://duck.co/duckduckhack/metadata for help in filling out this section.
name "PerldocCheatSheeet";
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";
# Uncomment and complete: https://duck.co/duckduckhack/metadata#category
# category "";
# Uncomment and complete: https://duck.co/duckduckhack/metadata#topics
# topics "";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/PerldocCheatSheet.pm";
attribution github => ["GitHubAccount", "Friendly Name"],
twitter => "twitterhandle";
# Triggers
triggers any => "triggerWord", "trigger phrase";
# Handle statement
handle remainder => sub {
# optional - regex guard
# return unless qr/^\w+/;
return unless $_; # Guard against "no answer"
return $_;
};
1;
我将填写摘要、元数据和触发器的答案,以及handle
子程序。
package DDG::Goodie::PerldocCheatSheet;
# ABSTRACT: A cheat sheet for perldoc, the Perl documentation program
use DDG::Goodie;
zci answer_type => "perldoc_cheat_sheet";
zci is_cached => 1;
# Metadata
name "PerldocCheatSheet";
source "http://perltricks.com/article/155/2015/2/26/Hello-perldoc--productivity-booster";
description "A cheat sheet for perldoc, the Perl documentation program";
primary_example_queries "help perldoc", "perldoc cheatsheet", "perldoc commands", "perldoc ref";
category "programming";
topics qw/computing geek programming sysadmin/;
code_url
"https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/PerldocCheatSheet.pm";
attribution github => ["dnmfarrell", "David Farrell"],
twitter => "perltricks",
web => 'http://perltricks.com';
# Triggers
triggers startend => (
"perldoc",
"perldoc help",
"help perldoc",
"perldoc cheat sheet",
"perldoc cheatsheet",
"perldoc commands",
"perldoc ref");
# Handle statement
my $HTML = share("perldoc_cheat_sheet.html")->slurp(iomode => '<:encoding(UTF-8)');
my $TEXT= share("perldoc_cheat_sheet.txt")->slurp(iomode => '<:encoding(UTF-8)');
handle remainder => sub {
return
heading => 'Perldoc Cheat Sheet',
html => $HTML,
answer => $TEXT,
};
1;
handle子程序将返回给用户的纯文本和HTML版本的速查表。share函数从share/goodie/目录加载静态文件。这些文件应位于share/goodie/perldoc_cheat_sheet/目录中,并且必须确保文件名是即时答案名称的小写版本,由下划线分隔。因此,“PerldocCheatSheet”变为“perldoc_cheat_sheet”。您可以在GitHub上查看文件。请注意,CSS文件没有直接由任何代码引用:它被DuckDuckGo自动加载(这就是为什么目录和文件名必须正确的原因)。我从tmux 示例中复制了CSS,它提供了两列文本,将并排显示或根据屏幕宽度太窄而换行到单列。
测试即时答案
测试即时答案是否正常工作的最快方法是使用duckpan query
命令。我可以在终端中运行它
$ duckpan query
这启动了一个交互式命令行程序。我可以输入我perldoc即时回答的触发器之一,看看服务器是否按预期响应。
Query: perldoc ref
You entered: perldoc ref
---
DDG::ZeroClickInfo {
Parents WWW::DuckDuckGo::ZeroClickInfo
public methods (4) : DOES, has_structured_answer, new, structured_answer
private methods (0)
internals: {
answer "perldoc [option]
Module Options
--------------
...
看起来不错!(我已经剪掉了输出,因为它很冗长)。接下来我可以尝试使用duckpan server
进行浏览器测试。
$ duckpan server
然后我将我的浏览器指向http://localhost:5000
,输入即时回答的触发查询。这也行得通。最后,我需要完成即时回答的单元测试脚本。我已经有了由duckpan new
在开始时创建的测试脚本框架。
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use DDG::Test::Goodie;
zci answer_type => "perldoc_cheat_sheet";
zci is_cached => 1;
ddg_goodie_test(
[qw( DDG::Goodie::PerldocCheatSheeet )],
# At a minimum, be sure to include tests for all:
# - primary_example_queries
# - secondary_example_queries
'example query' => test_zci('query'),
# Try to include some examples of queries on which it might
# appear that your answer will trigger, but does not.
'bad example query' => undef,
);
done_testing;
我将更新测试文件,并添加一些注释。
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use DDG::Test::Goodie;
zci answer_type => "perldoc_cheat_sheet";
zci is_cached => 1;
# all responses for this goodie are the same
my @test_zci = (
# regex for the plain text response
qr/^perldoc \[option\].*Module Options.*Search Options.*Common Options.*Help.*$/s,
# check the heading
heading => 'Perldoc Cheat Sheet',
# check the html pattern
html => qr#$#s,
);
ddg_goodie_test(
# name of goodie to test
['DDG::Goodie::PerldocCheatSheet'],
# At a minimum, be sure to include tests for all:
# - primary_example_queries
# - secondary_example_queries
'help perldoc' => test_zci(@test_zci),
'help perldoc' => test_zci(@test_zci),
"perldoc" => test_zci(@test_zci),
"perldoc help" => test_zci(@test_zci),
"help perldoc" => test_zci(@test_zci),
"perldoc cheat sheet" => test_zci(@test_zci),
"perldoc cheatsheet" => test_zci(@test_zci),
"perldoc commands" => test_zci(@test_zci),
# Try to include some examples of queries on which it might
# appear that your answer will trigger, but does not.
'perl doc help' => undef,
'perl documentaton' => undef,
'perl faq' => undef,
'perl help' => undef,
);
done_testing;
大部分都很容易理解;但也有一些需要注意的地方;@test_zci
是一个存储成功触发即时回答的预期输出的变量。这是一个有点黑客式的解决方案:它传递给test_zci()
函数,该函数期望一个与纯文本响应匹配的标量,后跟两个键值对,一个用于标题,一个用于HTML响应(更多详情请参见文档)。我可以在命令行运行此脚本。
$ prove -I t/PerldocCheatSheet.t
t/PerldocCheatSheet.t .. ok
All tests successful.
Files=1, Tests=12, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.17 cusr 0.01 csys = 0.20 CPU)
Result: PASS
所有测试都通过了,所以我准备向DuckDuckGo社区提交一个pull request!
寻求帮助的地方
虽然DuckDuckGo工具很棒,但也有一些好的文档和友好的社区在需要时支持开发。我在Gitter 聊天室上花了一些时间,即时回答仓库里的人都很友好、反应迅速(更重要的是,他们有commit权限:)。
这篇文章最初发布在PerlTricks.com上。
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开一个问题或pull request来帮助我们。