写在开始:
本文内讨论的Listary版本号为 6.0.1,目前最高版本似乎是 6.0.5。文中提到的插件,官方也似乎将接口换成了异步的版本,但是考虑到同步调用和异步调用本质上程序逻辑没有根本的变化,代码主逻辑还是可以一样地跑通,或者直接原封不动换回同步版本也是能用的。

某天下午:

—— “NICE! Listary的字典能用了!!”
——“啥是Listary?”
——“啊这……”

Listary 是一个革命性的Windows搜索工具,借助 Listary软件,你可以快速搜索电脑文件、定位文件、执行智能命令、记录访问历史、快速切换目录、收藏常用项目等。

—— 来自某乎的回答

笔者就是嫌有道词典太臃肿,必应词典太卡,用Listary能大致以随叫随到的小搜索框功能就能实现诸如文件搜索、翻译、简单计算等实用的功能。

—— “原来你也玩原神”
—— “这是重点吗?”

总之就是一个比较方便的小工具。当然也提供字典查词的功能…… 【本来是提供的
这搜索转圈可以转到你下班都转不完,根本读不出来内容。

这么好用的功能就这么无了???不,今天就来解救这个功能!

定位问题

打开Listary的设置,看了看字典是咋肥事,好家伙,是个插件,也没啥可以直接设置查询地址的地方,看来查询地址应该是在插件里写的,没法随意改动,那就得去改插件本身了。

直接对Listary进行了一番小调查,发现它的插件加载都在

C:\Users\<<username>>\AppData\Roaming\Listary\UserProfile\Extensions

这样一个文件夹里,Good,找到了一个叫做listary-extension-dict的文件夹,看起来就像是字典这个功能的文件夹,不错不错,直接打开,一个index.js映入眼帘,笔者人傻了,你一个Listary程序,从头到尾C#编译,最后插件编写来了个JavaScript

怒翻Listary的安装文件夹,好家伙,藏了个node.js在里面。额滴个神,笔者寻思插件也是C#呢,合着得用JavaScript改?

打开这个index.js,内容如下……【在6.0.5版本内容略有差异,无伤大雅,6.0.5版本的最终修改后代码也会在最后一并放出,TL,DR: 跳到最后】

const request = require('request');
const api = "http://www.iciba.com/index.php";function search(query) {var noResult = {title: "没有找到相关结果"};return new Promise((resolve, reject) => {var options = {method: 'GET',url: api,qs:{a: 'getWordMean',c: 'search',list: '1',word: query},};request(options, function (error, response, body) {if (error) throw new Error(error);var json =  JSON.parse(body);if (json.errno != 0) {resolve([{title: json.errmsg}]);return;}// console.log(json)if (json.baesInfo.symbols) {var results = json.baesInfo.symbols[0].parts.map(part => {var meaning = part.part + " ";meaning += part.means.join(";");var result = {title: meaning}return result;});resolve(results);}else if (json.baesInfo.translate_result) {resolve([{title: json.baesInfo.translate_result}])}else {resolve([{title: noResult}])}});});
}
module.exports = {search: search
};

第一眼,就印证了我之前的想法,果不其然api地址是写死的哇。把这个iciba相关地址(PHP是世界上最好的语言)直接放到浏览器里一看,芜湖,直接跳转到了主页,这API早就不好使了,这能查到词就有鬼了。

第二眼,// console.log(json)作者很细啊,把错误信息直接打印在了命令行里,后面一想反反正用户也看不到,注释了得了(狗头)。

第三眼,JavaScript有一点看不懂哇,不过编程无非那几招,ifvarnew还是能看得懂的,跟C#那可是 完全 一样的意思,细品一下嘛,说不定就会了。

第四眼,这花括号有点多,花里胡哨的,还中括号套花括号,眼睛有亿点抗不太住……

第五眼,… 第六眼,… …… ……

经过亿点的领悟,以及百度必应谷歌几位大佬的点拨,大致能看懂这货是在干什么了:

index.js 总览

这程序大致的框架分为三段:

  • 定义依赖库以及常量
  • 定义一个方法
  • 将方法返回

好家伙,直接返回一个方法,这不就是类似返回一个Func<>对象嘛。Easy。依赖库和常量部分就跳过了,我们先看看这个方法里定义了什么。

function search(query) { 在里面干了什么 }

以程序的视角,这在这个search(query)函数里,似乎就只干了两件事情:

  1. 定义了一个叫做 noResult 的变量
  2. 新构造了一个叫做Promise的类的实例并直接返回

noResult变量就不说了。直接看关键点,Promise类的实例,大致排版之后,这个实例的构造函数应该长这样:

new Promise
((resolve, reject) => {var options = {/* 省略 */};request(options, function (error, response, body) {/* 省略 */});}
)

这不就是一个 C# 里的匿名函数(lambda函数)嘛……这我可太熟悉了,经常往什么按钮点击、菜单点击时间里挂的不就是这玩意儿

(o, e) => { /*函数体省略*/ }

既然是个函数,那么这个函数的输入和输出类型就十分关键了,lambda函数的这特点就决定了这玩意儿可以省略类型,自动推导类型,emmm,直接进行一个 JavaScript 官网搜索Promise类的教学,哦吼,套起来了:

(resolve, reject) => { /* 函数体 */ }构造的函数的输入值,也就是resolvereject俩参数,他俩都是 函数


把函数作为参数传入另一个函数,嗯,好好品。

说人话版本就是你现在如果构造一个Promise对象给别人用的话,你需要别人提供 两个函数,然后你进行一番操作,要么 执行第一个函数【resolve】 ,或者就执行第二个函数【reject】 —— 这一番操作是在Promise对象的构造方法里的lambda函数内执行的。

套在我们当前的环节里:我们需要提供给Listary一个Promise对象,它要用这个Promise对象来执行搜索的操作。不过,我们能提供这个Promise对象的条件是:“Listary告诉我们两个函数”,我们搜索完了,要么执行函数一,要么执行函数二。【这里,函数一以及函数二是由Promise构造方法里的lambda函数里的输入参数,resolvereject提供的】。

也就是说,我们在这个Promise对象的构造方法的lambda里,需要进行一波操作:

  1. 获取搜索关键词
  2. 执行搜索,获取搜索结果
  3. 获取结果成功之后,执行Listary传来的resolve或者reject函数

这里,JavaScript约定了,一般来说,Promise内操作成功则需要执行resolve(也就是第一个传入参数的函数),若失败了(网络原因或者操作错误输入了错误的单词啥的)则需要执行reject(也就是第二个传入的参数)。笔者相信Listary的作者是按套路出牌的人,所以应该会执行这个JavaScript的设计原则,所以,笔者相信我们在搜词成功之后,应该调用resolve,搜索失败了应该就是调用reject

resolve() 的调用传参

好了,经过上面的缜密分析,笔者估计大家也十分地似懂非懂了,总而言之,我们在搜索单词成功之后呢,需要调用一下Listary那边由作者传过来的resolve函数。

BUT, HOW?我怎么知道要传什么参数进去才能成功调用?这里没有代码提示啊!!!

JavaScript 作为一门 动态类型语言,这种特性就决定了:即便是电脑,在真正拿到resolve这个对象之前,它***也不知道这个函数要咋运行。

谁知道呢?

只有写代码的人和上帝知道。
(抖个机灵:程序员写完这段代码之后三个月,就只剩下上帝知道了)

所以…… 直接模仿啊!看作者咋调用resolve的(直接对 “resolve” 关键词进行一个Ctrl+F的搜索):

resolve([{title: json.baesInfo.translate_result
}])

不错不错,又要学新东西了。这***是个啥?

不过,好歹是个函数调用嘛,先把括号给刨了,看看剩什么:

[{title: json.baesInfo.translate_result}
]

依据笔者对python的理解和json数据的理解,以及加上前面对JavaScript的学习,这玩意儿就是个装了对象的列表,[]是列表,里面的{}是一个对象的构造函数。这个对象包含了一个属性,属性名字为title,赋值为json.baesInfo.translate_result。上面针对于函数resolve的调用类似于:

public class MyObj {public string title;
}resolve(new object[] {new MyObj() { title = json.baesInfo.translate_result }
});

由于 JavaScript 动态类型的特性,压根不需要向上面C#一样定义MyObj这个类,也不需要提前定义类有哪些属性,花括号一用,直接就成一个动态类的对象了,对象内的属性直接写直接赋值就行,对象随便创建随便玩。妙啊!!!

所以每个在列表里的对象都会被翻译回来成为一个搜索结果,就像下面的一样:

好嘛,那如果搜索失败了呢?

resolve([{title: noResult
}])

但是前面的定义了noResult变量是{ title: "没有找到相关结果" },那这里岂不是套起来了?嗯,确实可能存在这样的问题,但是无所谓了,我们想要的了解的resolve()函数如何使用已经完成,下面就来看reject()

reject() 的调用传参

Listary作者似乎没有调用reject函数,挺好的省事儿了。

替换搜索API

终于,到了关键步骤了,我们已经知道了“如何处理搜索结果的数据”,现在的问题就是如何通过需要搜索的关键词来获取搜索结果了。这里当然是需要用到网上现有的API(笔者手里没有本地能用的查词API)。也就是说 ——

搞一个能提供翻译的API,把Listary原来的那个.php的地址换成能翻译的数据的API

这里不是我打广告啊,我直接选用了 “有道智云” 的“ 自然语言翻译服务-文本翻译 ”,当时注册直接送50,到现在接近2年了,我用了多少呢?

哇哈哈哈哈哈个人使用几乎没啥门槛的嘛…

注册过程就不说了,总之搜索一下,注册一下,开启“ 自然语言翻译服务-文本翻译 ”,拿到应用ID(AppKey)和应用密钥(Key)。

剩下的嘛,就是我们怎么把从function search(query) { }函数里的要搜索的关键词query送给有道智云(或者whatever翻译API)。这里观察了原来的Listary,是使用request发起了一个对某个地址的请求。那发送什么内容呢?这就只能找翻译API的服务供应商(这里就是有道智云)了呀,直接进行一个帮助文档的查找:

想仔细看的都仔细看看啊【接口调用参数 这部分还是很值得看的】,不想仔细看的直接跳转到最后的案例代码,找到JavaScript版本(JS版本)。

然后依葫芦画瓢,改改就完事儿了。总结起来就是我们需要:

  1. https://openapi.youdao.com/api 发起一个GET或者POST请求
    1.1 HTTPS协议
    1.2 GET/POST请求
    1.3 字符编码UTF8
  2. 调用的参数格式大致如下

    2.1 笔者对于源语言和目标语言一般直接就auto了
    2.2 salt生成方式和签名生成方式都需要依照API文档来执行

扯一大堆没用的,直接放出笔者的请求构造:

let api = "https://openapi.youdao.com/api";
let salt = (new Date).getTime();
let curtime = Math.round(new Date().getTime()/1000);
let str1 = appKey + truncate(decodeURIComponent(encodeURIComponent(query))) + salt + curtime + key;
let sign = CryptoJS.SHA256(str1).toString(CryptoJS.enc.Hex);var api_rq_options = {method: 'GET',url: api,qs: {q: decodeURIComponent(encodeURIComponent(query)), // 需要查询的单词appKey: appKey,     // 有道智云的“应用ID”salt: salt,         // 用时间生成的salt,为什么要加盐是个新知识,可以自行搜索from: 'auto',       // 翻译自什么语言to: 'auto',         // 翻译到什么语言sign: sign,         // 附带自己的应用密钥的加过盐的再带查询字符的一个摘要算法算完的 认证信息signType: "v3",     // 来自API说明文档,API文档说要写 v3curtime: curtime,   // 当前时间 用来送给服务器做验证},
};function truncate(q) { // 查词时生成摘要认证信息时需要用到的一个临时字符串处理函数 - 作用就是削减字符串长度,避免摘要计算时间过长 // 比如翻译一篇文章时,不想直接对一整篇文章做摘要算法处理var len = q.length;if (len <= 20) return q;return q.substring(0, 10) + len + q.substring(len-10, len);
}

这里操作了一大波encodeURIComponent,然后又decodeURIComponent,是为了确保输入的字符串可以以UTF8的格式来发送。

然后,直接使用 JavaScriptrequest函数对该API地址发起请求:

request(api_rq_options, function(error, response, body) { /* 此处继续省略函数实现 */ })

呱?又要写一个lambda函数??这里是一个完整的lambda函数了,不再是(a,b,c) => {}的形式了。具体就是向我们的api地址发送请求之后,针对于这个请求结果,会调用这个lambda函数,分别是

  • error错误时的错误码;
  • response成功了的返回码;
  • body返回的具体消息

但是…… 有道智云 有个很有趣的地方,这个它这个API永远不会返回“错误”,它返回错误也是一个“成功”,但是在body里会有个叫做errorCode的属性,用来具体显示有没有成功,如果出错了,这个码会有一个从101~17001不等的一个值来代表具体是什么错误。

不过whatever了,直接挨个处理就完了:

function (error, response, body)
{// 这里判断错误一般是系统性错误,比如连接超时了之类的,也就是检测非API错误if (error) throw new Error(error);var json = JSON.parse(body);if (json.errorCode > 100) // 判断是不是有错误……{// 依照前面的 resolve 函数,构造一个报错的返回查询结果resolve([{title : '查询出错: ' + json.errorCode}]);}// 好了,接下来就依照API的返回结果来构造结果列表,先来个空列表,然后往里挨个push翻译结果arr = [];if (json.basic) // 基本词义{for (let ind = 0; ind < json.basic.explains.length; ind++){arr.push({title : json.basic.explains[ind],subtitle : '基本词义',});}}if (json.web) // 网络词义{for (let index = 0; index < json.web.length; index++){let subtitle_str = '网络词义 - ' + json.web[index].key;for (let j = 0; j < json.web[index].value.length; j++) {arr.push({title: json.web[index].value[j], subtitle: subtitle_str, });}}}for (let i = 0; i < json.translation.length; i++) // 大段文字翻译结果{arr.push({title: json.translation[i], subtitle: '翻译结果'});}resolve(arr); // 调用resolve,处理arr这个列表结果
}

至此,我们对Listary的改造就完成了,让我们来查个词儿~

于是就有了开头那一幕。

芜湖,舒服了。继续板砖

【杂谈】 Listary自带的字典功能失效?没关系,让我们自己来改造它相关推荐

  1. vscode 格式化某一段代码_VSCode格式化代码功能失效的bug解决方法

    VSCode格式化代码功能失效的bug解决方法 前不久我装上了 黑苹果,那么为了快速转移开发环境,我使用了VSCode(Visual Studio Code下面简称VSCode)的插件 Setting ...

  2. 光模块功能失效的原因有哪些?

    光模块主要用来将设备(一般指的是交换机或者路由器设备)中的电信号转换成光信号,然后通过一根光纤发射出去(由光模块的发射端实现),同时可以接收外部一根光纤发射过来的光信号且转换成电信号(通过光模块的接收 ...

  3. vscode格式化html代码失效了,VSCode格式化代码功能失效的bug解决方法

    VSCode格式化代码功能失效的bug解决方法 前不久我装上了黑苹果,那么为了快速转移开发环境,我使用了VSCode(Visual Studio Code下面简称VSCode)的插件Settings ...

  4. win7 锁定计算机 失效,win7系统屏幕保护功能失效无法进入屏幕保护状态怎么办...

    win7系统自带屏幕保护功能,屏幕保护程序既能节省电量又能保护我们的显示器.如果打开电脑后临时有事,离开电脑后就会自动进入屏幕保护程序,将刚才工作的状态隐藏起来,非常好用.但是有用户反映说win7电脑 ...

  5. django自带的分页功能

    django自带的分页功能 django中自带的分页功能有缺陷,但是也是一种思路,所以在下做一个整理,方便以后使用,还有服务各位小伙伴. django视图部分的代码.(注释才是重点) from dja ...

  6. java实现倒计时闹钟_js带闹铃功能的倒计时代码

    Js倒计时代码,带闹铃功能,自定义闹钟倒计时功能,点击开始按钮,即可开始倒数,代码不是太复杂,新手应该能看懂,代码分享给大家. 效果图: 源码: Js倒计时,闹铃功能 function $(id){ ...

  7. php配置email支持_配置php自带的mail功能

    之前弄php的时候,需要录制屏幕,就是用php自带的email函数发信,因为没有自己弄过smtp的 服务器,网上提供的smtp服务器都是需要使用密码认证 的,偏偏php内置的函数就没有提供身份认证,也 ...

  8. tableau必知必会之拖拽功能失效是怎么回事

    大家反馈的问题如上图所示,可以发现,原本正常的拖拽功能失效了. 数据粉们别着急,这不是 Bug. 首先,我们来看一下你可能遇到的拖拽失效的几种情况: 一.维度.度量无法拖入标记卡 二.维度.度量无法拖 ...

  9. Pandas中兼并数组和字典功能的Series 2013-03-24 11:24:00 分类: Python/Ruby In [2]: # 这段代码用于并排显示多个Series对象 from it

    Pandas中兼并数组和字典功能的Series 2013-03-24 11:24:00 分类: Python/Ruby In [2]: # 这段代码用于并排显示多个Series对象 from iter ...

最新文章

  1. Android Studio 设置git 提交代码
  2. 电商后台:实例解读订单系统
  3. 拥抱.NET Core系列:MemoryCache 缓存过期
  4. spring aop实现原理_Spring 异步实现原理与实战分享
  5. linux java文件 core_linux下部署.net core/java
  6. linux png格式的文件,PNG文件结构分析之一(了解PNG文件存储格式)(转)
  7. 3月国内网站流量:腾讯夺第二 360安全中心降至第三
  8. Python中用format函数格式化字符串的用法(2.7版本讲解哦!)
  9. IE6 某些bug修复
  10. jquery ajax修改密码,提交form表单---修改密码 ajax、jQuery
  11. mybatis mysql net教程_MyBatis 教程
  12. 道路-水系河流-铁路-人口等栅格数据获取途径
  13. SQL Server 索引 之 书签查找 第十一篇
  14. 高考全国卷导数题分类
  15. MATLAB变声器程序
  16. 女神舒淇同款敏感肌,应该如何修复呢??
  17. 七夕节,我用代码制作了表白信封
  18. 厦门大学LaTeX模板:页眉页脚设置
  19. 具有系统权限的apk的安装和系统签名
  20. 240Hz超高刷新率加持,HKC疾风SG27C PLUS电竞显示器的使用新体验

热门文章

  1. html中seo三大标签,探索者SEO告诉你三大标签如何正确使用
  2. java中secretkey,java生成秘钥key,并保存秘钥到文件中
  3. mini2440----keil for AMR之IIC读写EEPROM(AT24C08)
  4. python dataframe 写入到doc文件_将Python Pandas DataFrame写入Word文档
  5. su PK sudo
  6. 华为荣耀平板5怎么样_荣耀平板5和华为m5青春版哪个好
  7. Candence学习篇(7)allegro pcb editor 17.2如何打开旧版本.brd和.dra文件等
  8. RoboCup仿真3D底层通信模块介绍(一)
  9. html实现读取读卡器,如何在web浏览器页面使用IC卡读卡器并且兼容所有浏览器
  10. 改进集束搜索(Refinements to Beam Search)